Codeforces Round #648 (Div. 2) / contest 1365


A B C D E F G

( √:做出; ●:尝试未做出; ○:已补题 )


题目地址:https://codeforces.com/contest/1365

感觉我最近做题错误率好高啊,这一次A、B、D都是WA过的,B最离谱WA了3次。



A Matrix Game

题意

思路

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

int r[55],c[55];

int main()
{
    //freopen("input.txt","r",stdin);
    int t=read();
    while(t--)
    {
        mem(r,0); mem(c,0);
        int n=read(),m=read();
        REP(i,1,n) REP(j,1,m)
        {
            if(read()) r[i]=c[j]=1;
        }
        int nr=0,nc=0;
        REP(i,1,n) if(!r[i]) nr++;
        REP(i,1,m) if(!c[i]) nc++;
        n=min(nr,nc);
        puts(n&1?"Ashish":"Vivek");
    }

    return 0;
}



B Trouble Sort

题意:数组中每个元素有两个值 ai 和 bi,其中 bi 只能取 0 或 1,两个元素能交换当且仅当 b i b j b_i\ne b_j ,问能否通过交换,使得数组的 a 单调不减。

思路:一开始想复杂了,后来忽然明白不一定要直接交换,可以有中间变量,所以只要 0 和 1 同时存在,那么通过中间变量的方式一定可以排好序;如果只存在一个,就要求一开始单调不减。

然而我的比赛时写的代码就复杂了。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

int n,a[505],b[505];

int main()
{
    //freopen("input.txt","r",stdin);
    int T=read();
    while(T--)
    {
        n=read();
        REP(i,1,n) a[i]=read();
        REP(i,1,n) b[i]=read();
        int n0=0,n1=0,flag=1;
        REP(i,1,n) n0+=b[i]==0,n1+=b[i]==1;
        REP(i,1,n-1)
        {
            int minx=1e9,k;
            REP(j,i,n) if(a[j]<minx) minx=a[j],k=j;
            if(k==i) continue;
            if(b[i]==b[k])
            {
                if(b[i] && !n0) flag=0;
                if(!b[i] && !n1) flag=0;
            }
            swap(a[i],a[k]); swap(b[i],b[k]);
        }
        puts(flag?"Yes":"No");
    }

    return 0;
}



C Rotation Matching

题意:a b 数组都是 1-n 的全排列,可以对 a 平移若干个单位,问每一种平移后 ( a i = = b i ) \sum (a_i==b_i) 的最大值是多少。

思路:如果我们把 a、b数组变成这样的意义:a[i] 表示原来 a 数组中 i 所在的下标,那么平移实际上就是把(现在的)a 数组每个位置都加上一个相同的值,ai==bi 的意义就是现在的 ai=bi(mod n)。所以就统计 (bi-ai)%n 的个数,取最大那个就行了。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=2e5+5;
int a[maxn],b[maxn],c[maxn],n;

int main()
{
    //freopen("input.txt","r",stdin);
    n=read();
    REP(i,1,n) a[read()]=i;
    REP(i,1,n) b[read()]=i+n;
    REP(i,1,n) c[(b[i]-a[i])%n]++;
    int ans=0;
    REP(i,0,n-1) ans=max(ans,c[i]);
    cout<<ans;

    return 0;
}



D Solve The Maze

题意:矩形地图中每个格子可能为 “."(空地)、”#“(墙壁)、”G“(好人)、”B"(坏人),地图中 (n, m) 为出口,现在可以把任意个空地变成墙,问是否存在方案,使得所有好人可以逃生,所有坏人都不能逃生。

思路:我一开始的想法就是把坏人的上下左右的空地都变成墙,然后思考了一下这么做的正确性:首先如果有一个好人一定要经过一个坏人的上下左右的某一格才能逃生,那么好人能逃生的同时坏人也可以,所以一定不存在,那么就说明这么做不影响好人的逃生,另外这么做又可以阻止坏人逃生,所以可行。这么做完之后就BFS统计一下判断一下就可以了。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=55;
int n,m,vis[maxn][maxn];
int dir[4][2]={0,1,0,-1,1,0,-1,0};
char a[maxn][maxn];

int main()
{
    //freopen("input.txt","r",stdin);
    int T=read();
    while(T--)
    {
        n=read(),m=read();
        REP(i,1,n) scanf("%s",a[i]+1);
        int nb=0,ng=0;
        REP(i,1,n) REP(j,1,m)
            if(a[i][j]=='B') nb++;
            else if(a[i][j]=='G') ng++;
        REP(i,1,n) REP(j,1,m) if(a[i][j]=='B')
        {
            REP(k,0,3)
            {
                int x=i+dir[k][0];
                int y=j+dir[k][1];
                if(a[x][y]=='.') a[x][y]='#';
            }
        }
        REP(i,1,n) REP(j,1,m) vis[i][j]=0;
        int mb=0,mg=0;
        typedef pair<int,int> P;
        queue<P> que;
        if(a[n][m]=='B') mb++;
        else if(a[n][m]=='G') mg++;
        if(a[n][m]!='#') que.push(P(n,m));
        vis[n][m]=1;
        while(!que.empty())
        {
            P p=que.front();que.pop();
            int xx=p.first,yy=p.second;
            REP(k,0,3)
            {
                int x=xx+dir[k][0],y=yy+dir[k][1];
                if(x<1 || x>n || y<1 || y>m || vis[x][y]) continue;
                if(a[x][y]=='G') mg++;
                else if(a[x][y]=='B') mb++;
                if(a[x][y]!='#') que.push(P(x,y)),vis[x][y]=1;
            }
        }
        puts((mg==ng && !mb)?"Yes":"No");
    }

    return 0;
}



E Maximum Subsequence Value

题意:题意好复杂

思路:简单来说就是猜到了 k 应该取 3,然后发现其实 k 更大是一种冗余。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

int n;
LL a[505],ans;

int main()
{
    //freopen("input.txt","r",stdin);
    n=read();
    REP(i,1,n) scanf("%lld",&a[i]);
    REP(i,1,n) REP(j,i+1,n) REP(k,j+1,n) ans=max(ans,a[i]|a[j]|a[k]);
    if(n==2) ans=max(ans,a[1]|a[2]);
    if(n==1) ans=max(ans,a[1]);
    cout<<ans;

    return 0;
}



F Swaps Again

题意:两个长度为 n 的数组 a 和 b,每次可以对 a 操作,操作过程为选择一个 k( k n 2 k\le \lfloor \frac{n}{2} \rfloor ),然后把 a 的长度为 k 的前缀和后缀交换,问是否存在方案,使得最后 a 和 b 相等。

思路:发现这种操作之后,原本对称的数操作之后还是对称的,也就是说这种操作不改变数组中数的一一对应,然后发现可以把对称的数通过操作放到任意其它对称的地方去。所以做法就是取出所有的对称的数,排个序,然后看 a 的和 b 的是否一样就行了。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=505;
int n,a[maxn],b[maxn];
struct node {int x,y;} c[maxn],d[maxn];
bool cmp(node a,node b)
{
    if(a.x==b.x) return a.y<b.y;
    return a.x<b.x;
}

int main()
{
    //freopen("input.txt","r",stdin);
    int T=read();
    while(T--)
    {
        n=read();
        REP(i,1,n) a[i]=read();
        REP(i,1,n) b[i]=read();
        int tot=0;
        for(int i=1,j=n;i<j;i++,j--)
        {
            c[tot]=(node){min(a[i],a[j]),max(a[i],a[j])};
            d[tot++]=(node){min(b[i],b[j]),max(b[i],b[j])};
        }
        sort(c,c+tot,cmp);
        sort(d,d+tot,cmp);
        int flag=1;
        REP(i,0,tot-1) if(c[i].x!=d[i].x || c[i].y!=d[i].y) {flag=0; break;}
        if((n&1) && a[n/2+1]!=b[n/2+1]) flag=0;
        puts(flag?"Yes":"No");
    }

    return 0;
}



G Secure Password

题意:这是一道交互题,有一个长度为 n 的数组 A,要设定一个密码 P,其中第 i 位密码等于除了 A[i] 之外的 。现在最多可以询问13次,每次给出一个数集,系统会返回 A 中下标在这个数集的所有数的 ,要求出密码。

思路:给 1-n 中每个数编码 m[1…n],使得每个编码所对应的集合都不是其它任意编码的子集(这里的意思是,如果说 x 对应的集合是 y 对应集合的子集,就表明 x 的二进制表示中 1 的位置的集合是 y 的子集)。那么每个下标就可以唯一地用二进制中 1 所在位置的集合表示。这样做的好处是,设 x[i] 表示下标对应编码中二进制第 i 位为 1 的所有数的或(形式化表述: x i = ( ( 1 < < i ) m j ) = 1 A j x_i=\bigvee\limits_{((1<<i)\wedge m_j)=1}A_j ),那么最后的密码 P i = ( ( 1 < < j ) m i ) = 0 x j P_i=\bigvee\limits_{((1<<j)\wedge m_i)=0}x_j (因为由 m[i] 的唯一性,它不是其他 m 的子集,它的唯一性由其 1 所在位置的集合决定,所以把它非 1 位置的 x 全部或起来,可以保证这个结果不包括 A[i],并且包括了其他所有 A)。

那么如果选取这个两两不包含的编码呢?

这里有一个 Sperner定理 ,大概的意思就是,从一个大小为 n 的集合 A 中选出子集的集合 S(也就是幂集的子集),使得 S 中所有的集合两两不包含,那么必然存在 S C ( n , n 2 ) |S|\le C(n,\lfloor\frac{n}2\rfloor) ,并且选择 S 的方法就是,把 A 的幂集中所有大小为 n 2 \lfloor\frac{n}2\rfloor 的集合拿出来组成 S 。(就不证明了(其实是不会)……)

这道题中可以发现 C(13, 6) 刚好满足要求,然后算 x 的时候正好询问 13 次。

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
    int x=0,flag=1;
    char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

int m[3005],cnt,n;
LL x[15],ans[1005];

void query(int d)
{
    vector<int> a;
    REP(i,0,cnt-1) if(i<n && (m[i]&(1<<d))) a.push_back(i);
    if(a.size()==0) return;
    printf("? %d ",a.size());
    for(int i:a) printf("%d ",i+1);
    puts("");
    fflush(stdout);
    scanf("%lld",&x[d]);
}

int main()
{
    //freopen("input.txt","r",stdin);
    REP(i,0,(1<<13)-1)
        if(__builtin_popcount(i)==6) m[cnt++]=i;
    n=read();
    REP(i,0,12) query(i);
    REP(i,0,n-1) REP(j,0,12) if(!((1<<j)&m[i])) ans[i]|=x[j];
    printf("! ");
    REP(i,0,n-1) printf("%lld ",ans[i]);
    puts("");
    fflush(stdout);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/dragonylee/article/details/106614319