Atcoder AGC006 题解

A - Prefix and Suffix

这个…暴力匹配就可以啦。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
const int N=105;
int n;char S[N],T[N];
int check(int l) {
	for(RI i=n-l+1;i<=n;++i)
		if(S[i]!=T[i-n+l]) return 0;
	return 1;
}
int main()
{
	scanf("%d",&n);
	scanf("%s",S+1),scanf("%s",T+1);
	for(RI i=n;i>=0;--i)
		if(check(i)) {printf("%d\n",2*n-i);break;}
	return 0;
}

B - Median Pyramid Easy

如果 x = 1 x=1 x=1或者 2 n − 1 2n-1 2n1,显然无解。

否则在金字塔的所有行的中间位置都放上 x x x

看向从下往上第一行的中间位置,既然第二行的中间位置也要是 x x x,则说明中间位置两边两个位置,要分别是一个比 x x x大的数和一个比 x x x小的数。

接着发现,如果三个数中出现了一个 x x x和一个比 x x x大的数,则它们的中位数一定是大于等于 x x x的数。那么第二行 x x x两边一定是一个大于等于 x x x的数和一个小与等于 x x x的数,再往上也是同样。

所以只要在中间位置摆上 x x x x x x的两边摆上 x − 1 x-1 x1 x + 1 x+1 x+1,剩下的位置就可以随便放了。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int n,x;
int main()
{
	scanf("%d%d",&n,&x);
	if(x==1||x==2*n-1) {puts("No");return 0;}
	puts("Yes");
	int now=0;
	for(RI i=1;i<n-1;++i) {
		++now;while(now==x-1||now==x||now==x+1) ++now;
		printf("%d\n",now);
	}
	printf("%d\n%d\n%d\n",x-1,x,x+1);
	for(RI i=1;i<n-1;++i) {
		++now;while(now==x-1||now==x||now==x+1) ++now;
		printf("%d\n",now);
	}
	return 0;
}

C - Rabbit Exercise

假设 p i p_i pi为第 i i i个点的期望位置,则执行一次操作 x x x后, p x p_x px将变为 2 p x + 1 − p x + 2 p x − 1 − p x 2 = p x + 1 + p x − 1 − p x \frac{2p_{x+1}-p_x+2p_{x-1}-p_x}{2}=p_{x+1}+p_{x-1}-p_x 22px+1px+2px1px=px+1+px1px

d i = p i − p i − 1 d_i=p_i-p_{i-1} di=pipi1,带入上面的式子算一下 d d d的改变情况,发现 d x d_x dx会变成 p x + 1 − p x p_{x+1}-p_x px+1px d x + 1 d_{x+1} dx+1会变成 p x − p x − 1 p_x-p_{x-1} pxpx1。那么这么这个操作相当于将 d x + 1 d_{x+1} dx+1 d x d_x dx交换位置。

先求出进行一轮操作后,在 i i i位置上的 d d d会被换到哪个位置。然后利用倍增求解 K K K轮操作,再通过 d d d还原出 p p p即可。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
typedef long long LL;
LL read() {
	LL q=0,w=1;char ch=' ';
	while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
	if(ch=='-') w=-1,ch=getchar();
	while(ch>='0'&&ch<='9') q=q*10+(LL)(ch-'0'),ch=getchar();
	return q*w;
}
const int N=100005;
int n,m,to[60][N];LL K,bin[60],p[N],d[N],a[N],b[N];
int work(int x) {
	for(RI i=59;i>=0;--i) if(K&bin[i]) x=to[i][x];
	return x;
}
int main()
{
	n=read();
	for(RI i=1;i<=n;++i) p[i]=read(),d[i]=p[i]-p[i-1];
	m=read(),K=read();
	for(RI i=1;i<=n;++i) a[i]=i;
	for(RI i=1;i<=m;++i) {int x=read();swap(a[x],a[x+1]);}
	for(RI i=1;i<=n;++i) to[0][a[i]]=i;
	bin[0]=1;for(RI i=1;i<=59;++i) bin[i]=bin[i-1]<<1;
	for(RI j=1;j<=59;++j)
		for(RI i=1;i<=n;++i) to[j][i]=to[j-1][to[j-1][i]];
	for(RI i=1;i<=n;++i) a[i]=work(i);
	for(RI i=1;i<=n;++i) b[a[i]]=i;
	for(RI i=1;i<=n;++i)
		p[i]=p[i-1]+d[b[i]],printf("%lld.0\n",p[i]);
	return 0;
}

D - Median Pyramid Hard

二分答案,将大于等于你二分出的这个答案的数都变成 1 1 1,否则变成 0 0 0

任意两个连续的 1 1 1的上面一定是两个连续的 1 1 1 0 0 0也同理。

对于一段 0101010101 0101010101 0101010101,我们发现往上一行,这一串两边会被 0 0 0或者 1 1 1“吞掉”一格(即这一串的两端会融入一段连续的0或者1中),中间的01则全部取反。

所以我们只要从中间往两边,找到第一次出现的连续01串,是0还是1即可。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
	int q=0;char ch=' ';
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
	return q;
}
const int N=100005;
int n,a[N<<1],b[N<<1];
int check(int x) {
	for(RI i=1;i<=2*n-1;++i) b[i]=(a[i]>=x);
	for(RI i=0;i<n;++i) {
		if(b[n-i]==b[n-i-1]) return b[n-i];
		if(b[n+i]==b[n+i+1]) return b[n+i];
	}
	return b[1];
}
int main()
{
	n=read();
	for(RI i=1;i<=2*n-1;++i) a[i]=read();
	int l=1,r=2*n-1,ans;
	while(l<=r) {
		int mid=(l+r)>>1;
		if(check(mid)) ans=mid,l=mid+1;
		else r=mid-1;
	}
	printf("%d\n",ans);
	return 0;
}

E - Rotate 3x3

很神仙的性质题啊。

首先发现每一次操作相当于交换你选择的矩阵的第一列和第三列,然后将三列都翻转过来。这也说明:

1.任何一列的三个数字不会被拆开到不同列中

2.第二行的数字不会去其他行,第一行和第三行的数字也不会到第二行

3.原来的奇数列操作后也应该在奇数列,原来的偶数列操作后也应该在偶数列。

于是我们可以判掉一些基本的不合法情况,对奇偶性列做黑白染色。

然后构造两种操作:

1.同色相邻两列同时翻转。

记大写字母与小写字母为两种正反状态,则:

abcde -> CBAde -> CBEDa -> ebcDa -> ebAdC -> aBEdC -> aBcDe

abcde -> aDCBe -> cdABe -> cbaDe -> ABCDe -> AbCde(执行上面那串操作)

2.交换同色相邻两列,正反状态不变,改变异色列中某一列的正反状态。这个只要直接选矩阵翻转,然后执行1操作即可达成。

2操作告诉我们,如果我们将两个白色列交换(无论是否相邻),都会导致黑色列中,当前正反状态和目标矩阵的正反状态不同的列的数量的奇偶性,发生改变。将两个黑色列交换同理。

于是我们这样交换一下,就得到了黑白列关于目标矩阵,正反状态不同的列的数量的奇偶性。

如果是偶数,则可以通过1状态全部消去。否则无法消去,所以出现一个奇数,则应该输出No。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
	int q=0;char ch=' ';
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
	return q;
}
const int N=100005;
int n,a[4][N],b[N],ans[2];
void NO() {puts("No");exit(0);}
int main()
{
	n=read();
	for(RI i=1;i<=3;++i)
		for(RI j=1;j<=n;++j) {
			a[i][j]=read();
			int x=(a[i][j]-1)%3+1,y=(a[i][j]+2)/3;
			if((i!=2&&x==2)||(i==2&&x!=2)) NO();
			if((j&1)^(y&1)) NO();
		}
	for(RI j=1;j<=n;++j) {
		int x=(a[1][j]-1)%3+1,y=(a[1][j]+2)/3;
		if((a[2][j]+2)/3!=y||(a[3][j]+2)/3!=y) NO();
		if(x==1&&(a[3][j]-1)%3+1!=3) NO();
		if(x==3&&(a[3][j]-1)%3+1!=1) NO();
		if(x==3) ans[j&1]^=1;
		b[j]=y;
	}
	for(RI i=1;i<=n;++i)
		while(b[i]!=i) ans[(i&1)^1]^=1,swap(b[i],b[b[i]]);
	if(ans[0]||ans[1]) NO();
	puts("Yes");
	return 0;
}

F - Blackout

将原题模型转化为,如果存在一个黑格子 ( x , y ) (x,y) (x,y),则从点 x x x向点 y y y连一条有向边,如果存在边 ( x , y ) (x,y) (x,y)和边 ( y , z ) (y,z) (y,z),则连边 ( z , x ) (z,x) (z,x),问最后边数。

我们假设边是无向的,这样分出若干连通块,在每个连通块里搞一下。

我们将一个连通块里的点三染色,使得红点连向蓝点,蓝点连向黄点,黄点连向红点。

如果无法完成染色,则该图会被连成完全图,贡献为 n 2 n^2 n2

如果只能染出一种或两种颜色,则不会加边,贡献为 m m m

否则所有红点会向所有蓝点连边,所有蓝点会向所有黄点连边,所有黄点会向所有红点连边。设 c 1 , c 2 , c 3 c_1,c_2,c_3 c1,c2,c3为三种颜色点的数量,贡献为 c 1 c 2 + c 2 c 3 + c 3 c 1 c_1c_2+c_2c_3+c_3c_1 c1c2+c2c3+c3c1

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
	int q=0;char ch=' ';
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
	return q;
}
typedef long long LL;
const int N=100005;
int h[N],ne[N<<1],to[N<<1],w[N<<1],col[N];
int n,m,tot,c1,c2,flag,js[5];LL ans;
void add(int x,int y,int z) {to[++tot]=y,ne[tot]=h[x],h[x]=tot,w[tot]=z;}
int dfs(int x,int cc) {
	col[x]=cc,++js[cc],++c1;
	for(RI i=h[x];i;i=ne[i]) {
		if(w[i]==1) ++c2;
		int nxt=(cc+w[i])%3==0?3:(cc+w[i])%3;
		if(!col[to[i]]) dfs(to[i],nxt);
		else if(col[to[i]]!=nxt) flag=1;
	}
}
int main()
{
	int x,y;
	n=read(),m=read();
	for(RI i=1;i<=m;++i) x=read(),y=read(),add(x,y,1),add(y,x,-1);
	for(RI i=1;i<=n;++i) {
		if(col[i]) continue;
		c1=c2=js[1]=js[2]=js[3]=flag=0;
		dfs(i,1);
		if(flag) ans+=1LL*c1*c1;
		else if(js[1]&&js[2]&&js[3])
			ans+=1LL*js[1]*js[2]+1LL*js[2]*js[3]+1LL*js[3]*js[1];
		else ans+=c2;
	}
	printf("%lld\n",ans);
	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值