Codeforces Round #709 (Div. 2, based on Technocup 2021 Final Round)(C~D)(队列模拟循环

C. Basic Diplomacy

这题的构造条件其实相当宽松。只要先判断一个人的只能选择那个人的天数是否大于(m+1)/2。(即对于1个数有11,11,11,11,212。那么只能选择1的天数为4天)如果有人满足这个条件,则返回NO,否则一定可以构造一个序列满足条件。因为若没有一个人的只能选择天数大于(m+1)/2.则将只能选择一个人的天数的人选敲定后,剩余的天数中一定可以选出一个不同的人来防止越界。

AC代码如下:

#include<bits/stdc++.h>
#define MAXN 100005
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
int t,n,m,cnt[MAXN],ans[MAXN];
vector<int>a[MAXN];
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        int len;
        for(int i=1;i<=m;++i){
            scanf("%d",&len);
            if(len==1){//如果这一天是只能选择一个人 
                int temp;
                scanf("%d",&temp);
                ++cnt[temp];//那个人的cnt++ 
                ans[i]=temp;//那一天的答案为temp 
                a[i].push_back(temp);
            }else{//否则的话 
                for(int j=0;j<len;j++){
                    int temp;
                    scanf("%d",&temp);
                    a[i].push_back(temp);//把这一天可以选择的人都列入vector 
                }
            }
        }
        int flag=0;
        for(int i=1;i<=n;++i)
            if(cnt[i]>(m+1)/2){//如果有个数的cnt大于了(m+1)/2.就说明不行 
                flag=1;
                break;
            }
        if(flag)
            printf("NO\n");
        else{//如果可行的话就能构造 
            for(int i=1;i<=m;++i){
                if(a[i].size()==1) continue;//如果该天只能选择一个人,就跳过 
                int pos=0;
                while(cnt[a[i][pos]]==(m+1)/2)
                    ++pos;/*找到第一个cnt小于(m+1)/2的。
                                                注意不可能有两个数都等于这个,
                                                否则的话就说明每天都只有一个人可以选择 */
                ++cnt[a[i][pos]];//找到后令这一天只能选这个数,即这个数的唯一选择天数++ 
                ans[i]=a[i][pos];//令这一天对应的答案为这个数 
            }
            printf("YES\n");
            for(int i=1;i<=m;++i)
                printf("%d ",ans[i]);//输出答案 
            printf("\n");
        }
        memset(ans,0,sizeof(ans));
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=m;i++)
            a[i].clear();
    } 
    return 0;
}

D. Playlist(队列维护)

用一个nex数组记录与该数对比的那个数下标。将所有的相邻两个gcd=1的数对入列,并将右边的数删除,再判断左边的数和右边的右边的数的gcd,再入列即可。由于队列先进先出的特点,故可以用来模拟圈中转完一轮再看下一轮的特点。需要注意的是需要先判断一下左边的那个数在前面有没有被删除过,被删除过了则continue即可。

AC代码如下:

#include<bits/stdc++.h>
#define MAXN 100005
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
int t,n,m,a[MAXN],nex[MAXN],del[MAXN],ans[MAXN];
int gcd(int a,int b){return (b==0)?a:gcd(b,a%b);}
queue<int>q;
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        memset(del,0,sizeof(del)); 
        memset(ans,0,sizeof(ans));
        for(int i=0;i<n;i++)//0~n-1 .nex数组的初始化 
            nex[i]=(i+1)%n;
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        for(int i=0;i<n;i++)
            if(gcd(a[i],a[nex[i]])==1)
                q.push(i);
        int cnt=0;
        while(!q.empty()){
            int u=q.front();
            q.pop();
            if(del[u]) continue;
            del[nex[u]]=1;
            ans[++cnt]=nex[u]+1;
            nex[u]=nex[nex[u]];//右边的数已经被删除了,更新右边的数 
            if(gcd(a[u],a[nex[u]])==1)
                q.push(u);
        }
        printf("%d ",cnt);
        for(int i=1;i<=cnt;i++)
            printf("%d ",ans[i]);
        printf("\n");
    } 
    return 0;
}

 

posted @ 2021-03-25 21:35  mikku  阅读(50)  评论(0)    收藏  举报