Codeforces #283 Div 1 簡要題解

比賽總結

算了不提了,比賽時只弄出來第一題,wa了7次,罰時跪得一塌糊塗,*了狗了。。。

A. Removing Columns

題目鏈接

http://codeforces.com/contest/497/problem/A

題目大意

給你n 個依次排列的長度爲m 的字符串,構成一個nm 大小的表格,每次你從中刪除一列。問最少刪除多少次,才能使得這些字符串是字典序的

思路

非常坑爹的細節題,其間不知道wa了多少次。。。。

顯然是個貪心,我們從第一列到最後一列掃,只要能保留的列,保留下來肯定對後面沒有影響。

如果當前在保留下來的列中,某些字符串的前綴都一樣:
AAAAAA?…..
AAAAAA?…..
…….
AAAAAA?…..
那麼?列就必須得按照字母非降排序,類似下面這樣
AAAAAA A…..
AAAAAA B…..
…….
AAAAAA E…..

否則這一列隨便什麼順序都行

我們可以維護一個數組mark[i]mark[i]=true 表示在保留下來的列中,第i 個字符串和第i+1 個字符串的前綴相同。顯然掃到某一列時,若mark[i]=true ,但是這一列的第i 行字母大於i+1 行的字母,則這一列顯然不能要,可以發現,如果這一列存在i,j(j>i) ,且第i 行字母大於j 行的字母,也存在i ,使得這一列的第i 行字母大於i+1 行的字母。其他情況下這一列可以保留

代碼

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 510

using namespace std;

char mp[MAXN][MAXN];
int n,m,maxans=0;
bool mark[MAXN];

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%s",mp[i]+1);
    int ans=0;
    for(int j=1;j<=m;j++)
    {
        bool flag=true;
        for(int i=1;i<n;i++)
        {
            if(!mark[i]&&mp[i][j]>mp[i+1][j])
            {
                ans++;
                flag=false;
                break;
            }
        }
        if(flag)
        {
            for(int i=1;i<n;i++)
                if(mp[i][j]<mp[i+1][j])
                    mark[i]=true;
        }
    }
    printf("%d\n",ans);
    return 0;
}

B. Tennis Game

題目鏈接

http://codeforces.com/contest/497/problem/B

題目大意

A和B打n 次羽毛球,給出每一次的勝負情況(1:A勝 2:B勝),一個人在這個遊戲中勝利需要滿足兩個條件:1、每一局最先贏t 個球 2、最先贏s 局。如果已經可以確定勝負,則比賽終止。輸出所有可能的合法(s,t) 組合。

思路

首先我們枚舉t ,然後看是否能正好打完n 次羽毛球並確定勝負。我們可以不斷地二分到了什麼時候,A和B中有一個人贏得了新的一局。然後記錄下A和B各自贏得的局數totA,totB ,如此反覆,若恰好用完了n 次回合,且totAtotB 不相等,而且贏了maxtotA,totB 局的那一方也恰好贏得了第n 次羽毛球(如果勝利者沒有贏得第n局,顯然是不科學的),那麼就能得到一個合法的(s,t) 組合

代碼

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <vector>

#define MAXN 210000

using namespace std;

int n;
int result[MAXN];
int sumA[MAXN],sumB[MAXN];

vector<pair<int,int> >sol;

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&result[i]);
    for(int i=1;i<=n;i++)
    {
        sumA[i]=sumA[i-1]+(result[i]==1);
        sumB[i]=sumB[i-1]+(result[i]==2);
    }
    for(int t=1;t<=n;t++)
    {
        int totA=0,totB=0,p=0;
        while(p<n)
        {
            int lowerBound=p+1,upperBound=n,ans=-1;
            while(lowerBound<=upperBound)
            {
                int mid=(lowerBound+upperBound)>>1;
                if(sumA[mid]-sumA[p]>=t||sumB[mid]-sumB[p]>=t)
                {
                    ans=mid;
                    upperBound=mid-1;
                }
                else lowerBound=mid+1;
            }
            if(ans==-1) break;
            if(sumA[ans]-sumA[p]>=t&&sumB[ans]-sumB[p]<t) totA++;
            if(sumA[ans]-sumA[p]<t&&sumB[ans]-sumB[p]>=t) totB++;
            if(ans==n&&totA!=totB)
            {
                if(totA>totB&&result[ans]!=1) break;
                if(totA<totB&&result[ans]!=2) break;
                sol.push_back(make_pair(max(totA,totB),t));
            }
            p=ans;
        }
    }
    sort(sol.begin(),sol.end());
    printf("%d\n",sol.size());
    for(int i=0;i<sol.size();i++)
        printf("%d %d\n",sol[i].first,sol[i].second);
    return 0;
}

C. Distributing Parts

題目鏈接

http://codeforces.com/contest/497/problem/C

題目大意

給你n 個紅色線段[Li,Ri] ,以及m 個藍色線段[Li,Ri] ,一個藍色線段[Li,Ri] 能覆蓋一個紅色線段[Li,Ri] ,當且僅當LiLiRiRi ,且一個藍色線段可以覆蓋ki 個不同的紅色線段,問是否能用藍色線段覆蓋掉所有的紅色線段,並輸出一種可行方案。

思路

比較簡單的貪心。我們首先將紅色線段和藍色線段按照第一關鍵字右端點升序、第二關鍵字左端點升序來排序,然後從左到右掃藍色線段i ,每次在set里加入右端點Ri 的紅色線段。這樣的話,我們就可以不必考慮右端點的影響,而且set裏保存的是沒有被刪除的、且右端點Ri 的紅色線段。然後我們在set裏lowerbound出Lj>=Li 的最小的紅色線段j,並在set裏刪去它。如此做ki 次直到找不出這樣的紅色線段爲止。顯然這樣的貪心是正確的,能覆蓋最多的紅色線段。

代碼

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <set>

#define MAXN 210000

using namespace std;

int n,m;

struct Segment
{
    int L,R,k,id;
}song[MAXN],musician[MAXN];

bool operator<(Segment a,Segment b)
{
    if(a.R==b.R)
        return a.L<b.L;
    return a.R<b.R;
}

int belong[MAXN];

struct Info
{
    int L,R,id;
    Info(){}
    Info(int _L,int _R,int _id):L(_L),R(_R),id(_id){}
};

bool operator<(Info a,Info b)
{
    if(a.L==b.L) return a.R<b.R;
    return a.L<b.L;
}

bool operator>(Info a,Info b)
{
    if(a.L==b.L)
        return a.R>b.R;
    return a.L>b.L;
}

multiset<Info>bst; //!!!
multiset<Info>::iterator it;

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&song[i].L,&song[i].R);
        song[i].id=i;
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&musician[i].L,&musician[i].R,&musician[i].k);
        musician[i].id=i;
    }
    sort(song+1,song+n+1);
    sort(musician+1,musician+m+1);
    int p=1,tot=0; //tot=被演奏的歌曲個數
    for(int i=1;i<=m;i++)
    {
        for(;p<=n&&song[p].R<=musician[i].R;p++)
        {
            //cout<<"L:"<<song[p].L<<" R:"<<song[p].R<<endl;
            bst.insert(Info(song[p].L,song[p].R,song[p].id));
        }
        for(int t=1;t<=musician[i].k;t++)
        {
            it=bst.lower_bound(Info(musician[i].L,0,0));
            if(it==bst.end()) break;
            belong[it->id]=musician[i].id;
            bst.erase(it);
            tot++;
        }
    }
    if(tot!=n)
    {
        printf("NO\n");
        return 0;
    }
    printf("YES\n");
    for(int i=1;i<=n;i++)
        printf("%d ",belong[i]);
    printf("\n");
    return 0;
}

D. Gears

題目鏈接

http://codeforces.com/contest/497/problem/D

題目大意

給你兩個多邊形,每個多邊形會以一個點爲中心順時針旋轉。兩個多邊形旋轉的速度(rad/s)相同,每個多邊形各有一個旋轉中心。
問這兩個多邊形旋轉過程中是否會發生碰撞

思路

觀察發現,碰撞發生時,肯定是一個多邊形的頂點撞上了另一個多邊形的邊。不妨設多邊形B的頂點撞上了多邊形A的邊(多邊形A的頂點撞上了多邊形B的邊的情況,只需要把A和B對換一下就行了)。我們不妨固定多邊形A不動,讓B繞着A做逆時針公轉,自己做順時針自轉,公轉和自轉的速率相同
這裏寫圖片描述
如上圖,設多邊形A的旋轉中心爲P,B的中心爲Q。則B公轉的軌道就是圖上藍色的、以P爲圓心、半徑爲PQ的圓。設黃色點Bi 爲碰撞點,得到C 點,C=P+QBi 。若點C 到線段AjAj+1 的距離小於等於PQ ,且並不是線段AjAj+1 上所有的點到點C 的距離小於等於PQ ,則A的邊AjAj+1 和B的點Bi 會發生碰撞。並不是很好證明,不過自己多畫幾個圖可能就比較好理解了。

代碼

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 1100

using namespace std;

typedef long long int LL;

struct Point
{
    LL x,y;
    Point(){}
    Point(LL _x,LL _y):x(_x),y(_y){}
}pA[MAXN],pB[MAXN],centerA,centerB;

int n,m;

Point operator-(Point a,Point b)
{
    return Point(a.x-b.x,a.y-b.y);
}

Point operator+(Point a,Point b)
{
    return Point(a.x+b.x,a.y+b.y);
}

LL cross(Point a,Point b)
{
    return a.x*b.y-a.y*b.x;
}

LL dist(Point a,Point b)
{
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}

struct Line
{
    Point st,ed;
    Line(){}
    Line(Point _st,Point _ed):st(_st),ed(_ed){}
};

bool check() //爲了防止被坑爹卡精度,所有的距離全部是實際距離的平方,面積也得是平方
{
    LL R=dist(centerA,centerB); //公轉軌道的圓的半徑
    for(int i=1;i<=m;i++) //枚舉B上的點i落到了A上面
    {
        Point tmpC=centerA+(pB[i]-centerB);
        for(int j=1;j<=n;j++) //枚舉A上的邊j->j+1
        {
            Point tmpA=pA[j],tmpB=pA[j%n+1];
            LL disA=dist(tmpA,tmpC),disB=dist(tmpB,tmpC);
            if(disA-R<=0&&disB-R>=0) //disA和disB中一個比R大,一個比R小,表明A上的邊j->j+1會碰撞上B上的點i
                return true;
            if(disA-R>=0&&disB-R<=0) //disA和disB中一個比R大,一個比R小,表明A上的邊j->j+1會碰撞上B上的點i
                return true;
            //if(disA-R<0&&disB-R<0) continue;
            if(max(disA,disB)*2>disA+disB+dist(tmpA,tmpB)) continue;
            if(cross(tmpC-tmpA,tmpB-tmpA)*cross(tmpC-tmpA,tmpB-tmpA)<=R*dist(tmpA,tmpB)&&disA>=R) //!!!!B上的點i到邊j->j+1的距離小於等於R
                return true;
        }
    }
    return false;
}

int main()
{
    scanf("%I64d%I64d",&centerA.x,&centerA.y);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%I64d%I64d",&pA[i].x,&pA[i].y);

    scanf("%I64d%I64d",&centerB.x,&centerB.y);
    scanf("%d",&m);
    for(int i=1;i<=m;i++) scanf("%I64d%I64d",&pB[i].x,&pB[i].y);

    if(check())
    {
        printf("YES\n");
        return 0;
    }

    swap(n,m);
    swap(centerA,centerB);
    swap(pA,pB);

    if(check())
    {
        printf("YES\n");
        return 0;
    }

    printf("NO\n");
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章