• AtCoder Grand Contest 006 题解


    传送门

    (A)

    咕咕

    const int N=105;
    char s[N],t[N];int n;
    inline bool eq(R int k){fp(i,1,k)if(s[n-k+i]!=t[i])return false;return true;}
    int main(){
    	scanf("%d%s%s",&n,s+1,t+1);
    	fd(k,n,0)if(eq(k))return printf("%d
    ",(n<<1)-k),0;
    	return 233;
    }
    

    (B)

    (x)放在最中间,然后在它左边放一个比它大的数,右边放两个比它小的数就行了

    具体证明在(D)题里

    const int N=5e5+5;
    int vis[N],st[N],a[N],top,n,x;
    int main(){
    	scanf("%d%d",&n,&x);
    	if(x==1||x==(n<<1)-1)return puts("No"),0;
    	puts("Yes");
    	if(x!=2){
    		a[n]=x,a[n-1]=x+1,a[n+1]=x-1,a[n+2]=x-2;
    		vis[x]=vis[x+1]=vis[x-1]=vis[x-2]=1;
    	}else{
    		a[n]=x,a[n-1]=x-1,a[n+1]=x+1,a[n+2]=x+2;
    		vis[x]=vis[x-1]=vis[x+1]=vis[x+2]=1;
    	}
    	fp(i,1,(n<<1)-1)if(!vis[i])st[++top]=i;
    	fp(i,1,n-2)a[i]=st[top--];
    	fp(i,n+3,(n<<1)-1)a[i]=st[top--];
    	fp(i,1,(n<<1)-1)printf("%d
    ",a[i]);
    	return 0;
    }
    

    (C)

    首先,假设操作的数为(i),左右分别为(x,y),那么操作之后的期望位置为((2x-i+2y-i)/2=x+y-i)

    (a_i)为原数组的差分数组,那么一次操作之后等价于交换(a_i,a_{i+1})

    因为(m)次操作等价于一个轮换,所以把轮换中的每个循环找出来就行了

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    typedef long long ll;
    const int N=1e5+5;
    int a[N],id[N],sz[N],bl[N],to[N],nid[N],n,m;ll sum[N],k;
    vector<int>st[N];
    inline void swap(R int &x,R int &y){R int t=x;x=y,y=t;}
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d",&n);
    	fp(i,1,n)scanf("%d",&a[i]);
    	fd(i,n,1)a[i]-=a[i-1],id[i]=i;
    	scanf("%d%lld",&m,&k);
    	for(R int i=1,x;i<=m;++i)scanf("%d",&x),swap(id[x],id[x+1]);
    	fp(i,1,n)to[id[i]]=i;
    	fp(i,1,n)if(!bl[i]){
    		bl[i]=i,++sz[i];
    		for(R int j=to[i];j!=i;j=to[j])bl[j]=i,++sz[i];
    		st[i].resize(sz[i]),st[i][0]=i,nid[i]=0;
    		for(R int j=to[i],c=1;j!=i;j=to[j],++c)st[i][c]=j,nid[j]=c;
    	}
    	fp(i,1,n)sum[st[bl[i]][(k+nid[i])%sz[bl[i]]]]=a[i];
    	fp(i,1,n)sum[i]+=sum[i-1];
    	fp(i,1,n)printf("%lld
    ",sum[i]);
    	return 0;
    }
    

    (D)

    首先二分答案,把大于等于(mid)的记为(1),其余的记为(0),那么就转化为判断顶上是(0/1)

    先判断两种特殊情况,(101010.....101),顶上必定为(1)(0101010....010),顶上必定为(0)

    为了方便把边界去掉,也就是说看成一个(n)(2n-1)列的网格,最终查询第一行第(n)列的值

    发现有如下性质

    (1.)假设有一段极长的全为(0/1)的段,记为([l,r]),且(l eq r),那么上面所有行中([l,r])这几列将一直为(0/1)

    (2.)假设有一段极长的(0/1)相间的段,记为([l,r]),且(l eq r),那么最终会在上面的某一行形成以(p)为分界的两个区间([l,p])([p+1,r]),且两个区间都满足性质(1)(p)的位置取决于这个区间的奇偶性)

    综上,最终所有区间都会形成满足性质(1)的区间,只要(O(n))求出所有区间的右端点,即可查询最终第一行第(n)列的值为(0/1)

    综上,总复杂度为(O(nlog n))

    ps:这里也就可以说明(B)的正确性了,只要找到一组使得(mid=x)时顶上为(1)(mid=x+1)时顶上为(0)的解即可

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int N=5e5+5;
    int a[N],b[N],st[N],col[N],n,t,l,r,mid,ans;
    bool ck(){
    	fp(i,1,n)b[i]=a[i]>=mid;
    	R int top=0;bool fl1=1,fl2=1;
    	fp(i,1,n)fl1&=(b[i]==(i&1)),fl2&=(b[i]!=(i&1));
    	if(fl1)return true;if(fl2)return false;
    	for(R int l=1,r=2;r<=n;l=r++){
    		if(b[r]==b[l]){
    			while(r+1<=n&&b[r+1]==b[l])++r;
    			st[++top]=r,col[top]=b[l];
    		}else{
    			while(r+1<=n&&((r+1-l)&1)==(b[r+1]^b[l]))++r;
    			if((r-l+1)&1)st[++top]=r,col[top]=b[l];
    			else st[++top]=(l+r)>>1,col[top]=b[l],st[++top]=r,col[top]=b[r];
    		}
    	}
    	assert(top);
    	fp(i,1,top)if(st[i-1]<=t&&st[i]>=t)return col[i];
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d",&n),t=n,n=(n<<1)-1;
    	fp(i,1,n)scanf("%d",&a[i]);
    	l=2,r=n-1;
    	while(l<=r){
    		mid=(l+r)>>1;
    		ck()?(ans=mid,l=mid+1):r=mid-1;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    (E)

    为什么我什么题都不会啊

    首先,我们发现同一列的三个数永远在同一列,且只会有正序和逆序两种情况,不妨分别记为(i)(-i)

    其次,一次操作可以看成是把三个数取反,并交换间隔的两个数

    那么我们把操作拆开来,一个是交换相隔的两个数(交换的过程中包括取反),一个是把某个数取反

    交换相隔的数,我们显然可以把奇偶分开考虑

    那么现在问题转化成给定一个排列,要把它变为有序,每一次可以交换相邻的两个数,求每一个数要交换多少次

    这个可以贪心,即先把最大的搞到最后,再把次大的搞到最后……

    那么每个数取反的次数,只要根据原来的情况和要交换的情况判断一下就好了

    最后回到原问题,我们发现奇数的一次交换对应于偶数的一次取反

    所以奇数的交换总次数要与偶数的取反总次数奇偶性相等

    同理偶数的交换总次数也要和奇数的交换总次数相等

    总复杂度(O(nlog n))

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int N=1e5+5;
    int n,a[N],b[N],c[N],f[N],g[N],id[N];
    struct BIT{
    	int c[N];
    	inline void clr(){memset(c,0,(n+1)<<2);}
    	inline void upd(R int x,R int y){for(;x<=n;x+=x&-x)c[x]+=y;}
    	inline int query(R int x){R int res=0;for(;x;x-=x&-x)res+=c[x];return res;}
    }t1,t2;
    bool ck(){
    	fp(i,1,n){
    		id[i]=(a[i]-1)/3+1;
    		if((i&1)!=(id[i]&1))return false;
    		R int x=(id[i]-1)*3+1,y=(id[i]-1)*3+2,z=(id[i]-1)*3+3;
    		if(a[i]==x&&b[i]==y&&c[i]==z){g[i]=(n<<1);continue;}
    		if(a[i]==z&&b[i]==y&&c[i]==x){g[i]=(n<<1|1);continue;}
    		return false;
    	}
    	return true;
    }
    int main(){
    	scanf("%d",&n);
    	fp(i,1,n)scanf("%d",&a[i]);
    	fp(i,1,n)scanf("%d",&b[i]);
    	fp(i,1,n)scanf("%d",&c[i]);
    	if(!ck())return puts("No"),0;
    	for(R int i=1;i<=n;i+=2)t1.upd(i,1);
    	for(R int i=n-(n&1^1);i>0;i-=2){
    		f[i]=t1.query(n)-t1.query(id[i])+t2.query(id[i]-1);
    		t1.upd(id[i],-1),t2.upd(id[i],1);
    	}
    	t1.clr(),t2.clr();
    	for(R int i=2;i<=n;i+=2)t1.upd(i,1);
    	for(R int i=n-(n&1);i>0;i-=2){
    		f[i]=t1.query(n)-t1.query(id[i])+t2.query(id[i]-1);
    		t1.upd(id[i],-1),t2.upd(id[i],1);
    	}
    	fp(i,1,n)g[i]=(g[i]-f[i])&1;
    	R int s=0,t=0;
    	for(R int i=1;i<=n;i+=2)s+=g[i];
    	for(R int i=2;i<=n;i+=2)t+=f[i];
    	t>>=1;
    	if((s&1)!=(t&1))return puts("No"),0;
    	s=0,t=0;
    	for(R int i=2;i<=n;i+=2)s+=g[i];
    	for(R int i=1;i<=n;i+=2)t+=f[i];
    	t>>=1;
    	if((s&1)!=(t&1))return puts("No"),0;
    	return puts("Yes"),0;
    }
    

    (F)

    又做不来系列

    先把一个黑点((x,y))转化成(x)(y)的一条有向边

    又因为两个弱连通块(把有向边视作无向边之后的连通块)之间互不影响,所以我们单独考虑一个弱连通块

    首先,如果存在自环,那么这个点所在的整个弱连通块中所有边都会相连(包括自环)

    这是因为所有和这个点存在单向边的点都会变成双向边,有双向边后又会形成自环,一直重复之后会把所有边都连起来

    判断自环的话,我们把原图进行(3)染色,那么会有三种情况

    • 不存在合法染色方案,那么必定有自环,答案就是该弱连通块的(size imes size)

    • 存在合法染色方案,但是有一种颜色没有出现,那么无法连上任何一条边

    • 否则的话,若(color[u]+1equiv color[v]pmod{3}),则((u,v))之间必有一条边,设(c[i])表示颜色为(i)的数的出现次数,则答案为(c[0] imes c[1]+c[1] imes c[2]+c[2] imes c[0])

    证明的话建议感性理解一下

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    typedef long long ll;
    const int N=1e5+5;
    struct eg{int v,nx,w;}e[N<<1];int head[N],tot;
    inline void add(R int u,R int v,R int w){e[++tot]={v,head[u],w},head[u]=tot;}
    int dis[N],c[3],eg,sz,fl,n,m;ll ans;
    void dfs(int u,int w){
    	dis[u]=w,++c[w],++sz;
    	go(u){
    		++eg;
    		if(dis[v]==-1)dfs(v,(dis[u]+e[i].w)%3);
    		else if(dis[v]!=(dis[u]+e[i].w)%3)fl=0;
    	}
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(R int i=1,u,v;i<=m;++i)scanf("%d%d",&u,&v),add(u,v,1),add(v,u,2);
    	memset(dis,-1,(n+1)<<2);
    	fp(i,1,n)if(dis[i]==-1){
    		eg=sz=c[0]=c[1]=c[2]=0,fl=1;
    		dfs(i,0);eg>>=1;
    		if(fl)ans+=(!c[0]||!c[1]||!c[2]?eg:1ll*c[0]*c[1]+1ll*c[1]*c[2]+1ll*c[2]*c[0]);
    		else ans+=1ll*sz*sz;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    boost.asio系列——buffer
    用boost.signal实现多播委托
    boost.asio系列——Timer
    通过boost.date_time进行时间运算
    STL文件读写基础
    boost的字符串处理函数——string algorithm
    boost.asio系列——socket编程
    boost.asio系列——io_service
    boost的字符串处理函数——format
    boost.circular_buffer简介
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11425170.html
Copyright © 2020-2023  润新知