Codeforces Round #632 (Div. 2) / contest 1333


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



A Little Artem

题意:给出一个n*m的网格(2≤n,m≤100),要把每个格子涂成黑色或者白色。记存在边相邻白格子的黑格子个数为B,存在边相邻黑格子的白格子个数为W,要求给出涂色方案,使得 B=W+1 。

思路:因为数据限制(大于等于2),故很容易想到只要把前 n-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 main()
{
    //freopen("input.txt","r",stdin);
    int t=read();
    while(t--)
    {
        int n=read(),m=read();
        REP(i,1,n)
        {
            if(i==n)
            {
                putchar('B');
                REP(i,2,m) putchar('W');
            }
            else
                REP(i,1,m) putchar('B');
            puts("");
        }
    }

    return 0;
}



B Kind Anton

题意:a 数组由 {-1, 0, 1} 组成,b数组由整数组成,在 a 数组上操作,每次操作可以选 i<j ,然后令 a[j]+=a[i],问能否在有限次操作下,把 a 变为 b 。

思路:不难发现,对于每个 i>1 ,如果 b[i]>a[i],那么 a 数组中 i 前面的位置只要存在 1 就可以得到 b[i],其它情况同理,对于 i=1,必须要 a[1]=b[1] 。所以先处理每个位置前面是否存在 -1 和 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;
}

const int maxn=1e5+5;
int a[maxn],f[maxn][2],b[maxn];

int main()
{
    //freopen("input.txt","r",stdin);
    int T=read();
    while(T--)
    {
        int n=read();
        REP(i,1,n) a[i]=read();
        REP(i,1,n) b[i]=read();
        REP(i,1,n)
        {
            if(f[i-1][0] || a[i-1]==-1) f[i][0]=1;
            else f[i][0]=0;
            if(f[i-1][1] || a[i-1]==1) f[i][1]=1;
            else f[i][1]=0;
        }
        int flag=1;
        REP(i,1,n)
        {
            if(b[i]>a[i] && !f[i][1]) flag=0;
            if(b[i]<a[i] && !f[i][0]) flag=0;
        }
        puts(flag?"YES":"NO");
    }

    return 0;
}



C Eugene and an array

题意:一个数列是 good 的,当且仅当它不存在某个子列(子列定义为原数列前后各删去若干个数(可以为0个)所得到的数列),使得这个子列求和为 0 。现在给出一个数列,问它有多少个 good 的子列。

思路:一开始思路有点混乱,后来想到只要对于每个左边界 L,能够计算出最小的 R,使得 [L, R] 中存在坏数列,那么就可以计算出反面的答案了。不过这种思路还是有点问题,就是很难算出 R。再后来换一个方向,对于每个右边界 R,计算出最大的 L,然后发现这样就容易很多。对于某个 R,假设原数列的前缀和为 s 数列,那么找到最右边的 L,使得 s[R]==s[L-1],则 [L, R] 这个区间的子列为 bad 的,进一步维护一直以来 L 的最大值 maxL,那么只要 l<maxL,[l, R] 都是 bad 的,也就是能算出反面的答案。

题解给出的做法是对于每个 L,求出最大的 R,使得 [L, R] 中没有重复元素,这样好像简单一点点。

代码

#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;
LL s[maxn];
map<LL,int> t;

int main()
{
    //freopen("input.txt","r",stdin);
    int n=read(),maxl=0;
    LL ans=0;
    REP(i,1,n) s[i]=s[i-1]+read();
    REP(r,0,n)
    {
        maxl=max(maxl,t[s[r]]);
        ans+=maxl;
        t[s[r]]=max(t[s[r]],r+1);
    }
    cout<<(1ll*n*(n+1)/2-ans);

    return 0;
}



D Challenges in school №41

题意:n(2≤n≤3000) 个人坐成一排,每个人的头要么向右看,要么向左看。在每一秒中,互看(左边向右,右边向左)的两个人可以一起把头扭过去(反向),这一秒钟每个人最多扭一次头,多个人可以同时扭。给出正整数 k,问是否存在一个方案,使得刚好 k 秒钟过去后,不存在相互看的人。

思路:简化题意,就是给出01串,每次可以把 10 变成 01 ,最后要变成 00...0011...111 。如果没有刚好 k 秒的限制,那么这题暴力 O ( n 2 ) O(n^2) 就可以做出来了,现在麻烦的是要刚好 k 次。先按照最优策略,也就是每次处理尽量多的对儿,求出一个次数 minx,然后如果把每对儿都当做一次来处理,又有一个答案 maxx,如果 m i n x k m a x x minx\leq k \le maxx ,那么说明存在方案。

尽管我知道应该怎么分配,前面尽可能拆,后面尽可能不拆,但是写起来真的是难写。

代码

#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=3e6+5;
int a[maxn],n,k,tot,sum;
vector<int> ans[maxn];
char s[maxn];

bool solve()
{
    tot++;
    REP(i,1,n-1) if(a[i] && !a[i+1])
    {
        ans[tot].push_back(i);
        swap(a[i],a[i+1]);
        i++;
    }
    sum+=ans[tot].size();
    return ans[tot].size()!=0;
}

int main()
{
    //freopen("input.txt","r",stdin);
    n=read(),k=read();
    scanf("%s",s+1);
    REP(i,1,n) a[i]=s[i]=='R';
    while(solve()); tot--;
    //cout<<tot<<' '<<_tot<<endl;
    if(k<tot || k>sum) return puts("-1"),0;

    int u=tot,i=1,j=0;
    while(u<k)
    {
        if(j>=ans[i].size()) j=0,i++;
        printf("1 %d\n",ans[i][j]);
        if(j<ans[i].size()-1) u++;
        j++;
    }
    printf("%d ",ans[i].size()-j);
    while(j<ans[i].size()) printf("%d ",ans[i][j++]);
    puts("");
    for(i++;i<=tot;i++)
    {
        printf("%d ",ans[i].size());
        REP(j,0,ans[i].size()-1) printf("%d ",ans[i][j]);
        puts("");
    }

    return 0;
}



E Road to 1600

题意:给定一个 N,要求一个 N*N 的网格,里面的数字是从 1 到 N*N,并且满足:一颗棋子从 1 开始走,每次选择这颗棋子没有走过,能走到,并且数字最小的位置,作为下一次走到的位置,如果某些时候不存在能走到并且没走过的位置,那么就花费 1 直接跳转到最小的没有走过的位置,这里 “能走到” 是由这个棋子的属性定义的,车可以横竖走,皇后可以横竖撇捺走。要求给出一个填充数字的策略,使得车走完全部格子的花费严格小于皇后。

思路:这些构造题一般都显得比较巧妙。

如果能找到一个最小的存在方案的边长 M,对于边长大于M的时候,只要把最大的 M*M 个数以相同的规律放在右上角,然后其它位置填充更小的数,保证能不用任何花费就可以到达右上角就行了。问题就在于如果寻找这个最小的 M。

我是猜测当 N=3 时存在方案(1,2肯定不存在),因为就 9 个数,我写了一份代码去寻找 N=3 时的方案:

#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 vis[12],b[12],a[4][4];
int visr[4][4],visq[4][4];

void print()
{
    REP(i,1,3)
    {
        REP(j,1,3) cout<<a[i][j]<<' ';
        cout<<endl;
    }
    cout<<endl;
}

bool can()
{
    int cr=0,cq=0,xr,yr,xq,yq,nxr,nyr,nxq,nyq;
    REP(i,1,3) REP(j,1,3) if(a[i][j]==1) xr=xq=i,yr=yq=j;
    mem(visr,0); mem(visq,0);
    visr[xr][yr]=visq[xq][yq]=1;
    REP(t,2,9)
    {
        int mr=100,mq=100;
        REP(i,1,3) REP(j,1,3)
        {
            if(!visr[i][j] && (i==xr || j==yr) && a[i][j]<mr) mr=a[i][j],nxr=i,nyr=j;
            if(!visq[i][j] && (i==xq || j==yq || abs(i-xq)==abs(j-yq)) && a[i][j]<mq)
                mq=a[i][j],nxq=i,nyq=j;
        }
        if(mr<10) xr=nxr,yr=nyr;
        else
        {
            REP(i,1,3) REP(j,1,3) if(!visr[i][j] && a[i][j]<mr)
                mr=a[i][j],xr=i,yr=j;
            cr++;
        }
        if(mq<10) xq=nxq,yq=nyq;
        else
        {
            REP(i,1,3) REP(j,1,3) if(!visq[i][j] && a[i][j]<mq)
                mq=a[i][j],xq=i,yq=j;
            cq++;
        }
        visr[xr][yr]=visq[xq][yq]=1;
    }
    return cr<cq;
}

void dfs(int x)
{
    if(x>9)
    {
        REP(i,1,3) REP(j,1,3) a[i][j]=b[(i-1)*3+j];
        if(can()) print();
        return;
    }
    for(int i=1;i<=9;i++) if(!vis[i])
    {
        vis[i]=1; b[x]=i;
        dfs(x+1);
        vis[i]=0;
    }
}

int main()
{
    //freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
    dfs(1);

    return 0;
}

然后发现有好多好多方案都满足,比如:

1 2 4 
5 3 8 
9 6 7 

1 2 5 
4 3 8 
9 7 6 

8 6 2 
4 1 7 
9 5 3

这些都可以。

代码

#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][505];
int ker[3][3]={1,2,4,5,3,8,9,6,7};

int main()
{
    //freopen("input.txt","r",stdin);
    n=read();
    if(n<3) return puts("-1"),0;
    int dir=n&1,t=0;
    REP(x,1,n-3)
    {
        if(dir)
        {
            REP(i,1,n-x+1) a[i][x]=++t;
            REP(j,x+1,n) a[n-x+1][j]=++t;
        }
        else
        {
            REP_(j,n,x+1) a[n-x+1][j]=++t;
            REP_(i,n-x+1,1) a[i][x]=++t;
        }
        dir^=1;
    }
    REP(i,0,2) REP(j,0,2) a[i+1][n-2+j]=t+ker[i][j];
    REP(i,1,n)
    {
        REP(j,1,n) printf("%d ",a[i][j]);
        puts("");
    }

    return 0;
}



F Kate and imperfection

题意:给定一个集合 M,这个集合的 imperfection 定义为里面两两最大公因数的最大的那一个,也就是 i m p e r f e t i o n ( M ) = max i , j M , i j g c d ( i , j ) imperfetion(M) = \max\limits_{i,j\in M,i\neq j}gcd(i,j) 。给出一个集合 S, I k I_k 定义为 S 的大小为 k 的子集,且 imperfection 的最小值。现给出 S={1, 2, …, n},求 I 2 , I 3 , . . . , I n I_2,I_3,...,I_n

思路:可以看出 I k I_k 就是在 I k 1 I_{k-1} 的方案上增加一个数得来的,所以是一个从小到大的处理。一开始肯定把 1 和所有质数选择,这样这些数的 imperfection就是1,然后考虑加哪些数使得 imperfection 为 2,然后是 3、4等等。注意到按照贪心策略每次添加一个非质数的时候,这个数的除本身外的最大因数一定是已选集合中某个数的因子,所以我们就把所有数按照最大因子排序,依次添加,每次添加后答案就更新为这个最大因子。

换个方向想,从前往后枚举 i,那么当前 i*2, i*3, … 的最大因子都是 i,这不就是埃氏筛的过程吗。筛一遍,排个序,就可以了。

代码

#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=5e5+5;
int a[maxn];

int main()
{
    //freopen("input.txt","r",stdin);
    int n=read();
    REP(i,2,n) for(int j=i*2;j<=n;j+=i) a[j]=i;
    REP(i,1,n) if(!a[i]) a[i]=1;
    sort(a+1,a+n+1);
    REP(i,2,n) printf("%d ",a[i]);

    return 0;
}

猜你喜欢

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