「AtCoder Beginner Contest」#264

ABC264.

A. "atcoder".substr()

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
signed main() {
	string s="atcoder";
	int l=read()-1, r=read()-1;
	for(;l<=r;++l) printf("%c",s[l]);
	puts("");
}

B. Nice Grid

不会结论,写了大暴力。把行折半之后大力判断。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
signed main() {
	int r=read(), c=read();
	if(r>=8) r=16-r;
	if(r==1) puts("black");
	else if(r==8) if(c&1) puts("black"); else puts("white");
	else {
		if(r&1) {
			if(r==3) if(c!=2&&c!=14) puts("black"); else puts("white"); 
			else if(r==5) if(c!=2&&c!=14&&c!=4&&c!=12) puts("black"); else puts("white"); 
			else if(r==7) if(c!=2&&c!=14&&c!=4&&c!=12&&c!=6&&c!=10) puts("black"); else puts("white"); 
		} else {
			if(r==2) if(c!=1&&c!=15) puts("white"); else puts("black");
			else if(r==4) if(c!=1&&c!=15&&c!=3&&c!=13) puts("white"); else puts("black");
			else if(r==6) if(c!=1&&c!=15&&c!=3&&c!=13&&c!=5&&c!=11) puts("white"); else puts("black");
		}
	}
}

C. Matrix Reducing

显然对于任意 \(i\)\(B_i\) 中每一个元素都要在 \(A\) 中某一行 \(j\) 全部出现,且若 \(i' > i\),则 \(j' > j\)。否则无解。

然后如同

6 7 8 9 10
16 17 18 19 20
    
6 8 9
16 18 19

\(B\) 中每一行的元素在 \(A\) 中对应行的位置,必须完全相同。样例中都是1 3 4

就这么点东西,实现的时候有亿点细节。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
int n1, m1, n2, m2, a[15][15], b[15][15], c[15][15];
int lst;
multiset<int> st[15];
signed main() {
	n1=read(), m1=read();
	for(int i=1;i<=n1;++i) for(int j=1;j<=m1;++j) {
		a[i][j]=read();
		st[i].insert(a[i][j]);
        // multiset维护每一行出现的数字
	}
	n2=read(), m2=read();
	for(int i=1;i<=n2;++i) for(int j=1;j<=m2;++j) b[i][j]=read();
	for(int i=1;i<=n2;++i) {
		int fg=0;
		for(int j=lst+1;j<=n1;++j) if(st[j].count(b[i][1])) {
			st[j].erase(st[j].find(b[i][1]));
            // 存在第一个元素的话就尝试找所有元素
            // 记得找完之后删除,因为可能有重复的
			bool ok=1;
			for(int k=2;k<=m2;++k)
				if(!st[j].count(b[i][k])) { ok=0; break; }
                // 有一个数字不存在,直接放弃这行
				else st[j].erase(st[j].find(b[i][k]));
			if(ok) { fg=j; break; }
		}
		if(!fg) { puts("No"); return 0; }
		else {
			for(int j=1;j<=m2;++j) for(int k=1;k<=m1;++k) {
				if(b[i][j]==a[fg][k]) c[i][j]=k;
                // 记录位置
			}
			lst=fg;
            // 下一行要从fg+1开始找
		}
	}
    // 下面这两块都是判断c[i][j]是否完全相同。
	for(int i=1;i<=n2;++i) {
		int pre=0;
		for(int j=1;j<=m2;++j) {
			if(pre>c[i][j]) { puts("No"); return 0; }
			else pre=max(pre,c[i][j]);
		}
        // 看有没有逆序对,有逆序对也是无解
	}
    // 看看对应位置,所有行是否相同
	for(int j=1;j<=m2;++j) {
		int fg=c[1][j];
		for(int i=2;i<=n2;++i) if(c[i][j]!=fg) { puts("No"); return 0; }
	}
	puts("Yes");
}

D. "redocta".swap(i,i+1)

求逆序对个数。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
int trans(char c) {
	if(c=='a') return 1;
	else if(c=='t') return 2;
	else if(c=='c') return 3;
	else if(c=='o') return 4;
	else if(c=='d') return 5;
	else if(c=='e') return 6;
	else if(c=='r') return 7;
	return 114514;
}
int a[10];
signed main() {
	string s; cin>>s;
	s=" "+s;
	for(int i=1;i<=7;++i) a[i]=trans(s[i]);
	int ans=0;
	for(int i=1;i<=7;++i) for(int j=i+1;j<=7;++j) {
		if(a[i]>a[j]) ++ans;
	}
	printf("%lld\n",ans);
}

E. Blackout 2

经典套路,删边转化为倒序加边,于是乎问题变为维护连通性和连通块信息。

\(sz_i\) 为连通块 \(i\) 的大小,\(d_i\) 为连通块 \(i\) 包含的 power plants 的数量。

考虑合并两个连通块 \(i\)\(j\),如果 \(d_i > 0\) 并且 \(d_j > 0\),那么合并之后不会产生任何贡献,因为在合并之前,\(i\)\(j\) 之中的城市就已经连接到各自的 power plants 了。

否则,如果 \(d_i =0\)\(d_j = 0\),那么合并之后也没有贡献。

\(d_i = 0\)\(d_j > 0\) 时,贡献为 \(sz_i\),否则为 \(sz_j\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
const int N=1e6+5;
int n, m, e, Q, dlt, u[N], v[N], q[N], d[N], fa[N], sz[N], ans[N];
bool vv[N];
int get(int x) { return x==fa[x]? x:fa[x]=get(fa[x]); }
void merge(int x,int y) {
	x=get(x), y=get(y);
	if(x!=y) {
		if(!d[x]&&d[y]) dlt+=sz[x];
		else if(!d[y]&&d[x]) dlt+=sz[y];
		fa[x]=y, sz[y]+=sz[x], d[y]+=d[x];
	}
}
signed main() {
	n=read(), m=read(), e=read();
	for(int i=1;i<=m;++i) d[n+i]=1;
	for(int i=1;i<=n+m;++i) fa[i]=i, sz[i]=1;
	for(int i=1;i<=e;++i) {
		u[i]=read(), v[i]=read();
	}
	Q=read(); 
	for(int i=1;i<=Q;++i) q[i]=read(), vv[q[i]]=1;
	for(int i=1;i<=e;++i) if(!vv[i]) merge(u[i],v[i]);
    // 合并没有被删的边
	ans[Q]=dlt;
    // ans[i]表示第i次删除之后的信息,所以只需要加边[q[2],q[Q]]即可
	for(int i=Q;i>1;--i) {
		merge(u[q[i]],v[q[i]]);
		ans[i-1]=dlt;
	}
	for(int i=1;i<=Q;++i) printf("%lld\n",ans[i]);
}

F. Monochromatic Path

分析

显然,对于一行或者一列,修改次数只有 \(0\)\(1\)

\(f(i,j,p=0/1,q=0/1)\) 表示,从 \((1,1)\) 到达 \((i,j)\),且第 \(i\) 行的修改次数为 \(p\),第 \(j\) 列的修改次数为 \(q\),所需的最小代价。

通过状态记录的信息就能得出,此时 \((i,j)\) 的数字为 \(c1 = a_{i,j} \oplus p \oplus q\)。顺便说一句这是因为异或是异或的逆运算,异或两次等于没异或。

同时也就得到了 \((i+1,j)\)\((i,j+1)\) 的颜色 \(c2\),分别为 \(a_{i+1,j} \oplus q\)\(a_{i,j+1} \oplus p\)

这样就能够转移了。若颜色相同则代价不变,否则加上修改那一行/列的代价。 $$ \[\begin{cases} f(i,j,p,q) \rightarrow f(i+1,j,0,q) & c1 = c2 \\ f(i,j,p,q) + r_{i+1} \rightarrow f(i+1,j,1,q) & c1 \neq c2 \end{cases}\]

$$

\[ \begin{cases} f(i,j,p,q) \rightarrow f(i,j+1,p,0) & c1=c2 \\ f(i,j,p,q) \rightarrow f(i,j+1,p,q) & c1 \neq c2 \end{cases} \]

最终答案 \[ \min_{i=0}^1 \min_{j=0}^1 \{ f(n,m,i,j) \} \]

CODE

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
const int N=2005;
int n, m, a[N][N], r[N], c[N];
int f[N][N][2][2];
#define rep(i,j,k) for(int i=j;i<=k;++i)
signed main() {
	n=read(), m=read();
	rep(i,1,n) r[i]=read();
	rep(i,1,m) c[i]=read();
	rep(i,1,n) rep(j,1,m) scanf("%1d",&a[i][j]);
	memset(f,0x3f,sizeof(f));
	f[1][1][0][0]=0, f[1][1][1][0]=r[1], f[1][1][0][1]=c[1];
	f[1][1][1][1]=r[1]+c[1];
    // 初始值
	rep(i,1,n) rep(j,1,m) rep(p,0,1) rep(q,0,1) {
		if(i+1<=n) {
			int c1=a[i][j]^p^q, c2=a[i+1][j]^q;
			if(c1==c2) f[i+1][j][0][q]=min(f[i+1][j][0][q],f[i][j][p][q]);
			else f[i+1][j][1][q]=min(f[i+1][j][1][q],f[i][j][p][q]+r[i+1]);
		}
		if(j+1<=m) {
			int c1=a[i][j]^p^q, c2=a[i][j+1]^p;
			if(c1==c2) f[i][j+1][p][0]=min(f[i][j+1][p][0],f[i][j][p][q]);
			else f[i][j+1][p][1]=min(f[i][j+1][p][1],f[i][j][p][q]+c[j+1]);
		}
	}
	int ans=1e15;
	rep(i,0,1) rep(j,0,1) ans=min(ans,f[n][m][i][j]);
	printf("%lld\n",ans);
}

「AtCoder Beginner Contest」#264
https://yozora0908.github.io/2022/abc264-solution/
作者
yozora0908
发布于
2022年8月15日
许可协议