ECfinal2021部分题解

把赛中卡住的题争取补一下
题目链接:https://codeforces.com/gym/103861

H. Check Pattern is Good

网络流
先把各自按奇偶反色,然后就变成要最多的全黑或全白
建立一个两侧各是n*n的二分图
左边表示将这个小矩形,右边表示将这个小矩形染成黑色
相交的黑白小矩形之间连接inf
如果还有希望变白的小矩形,就让s连到它,流量为1
如果还有希望变白的小矩形,就让它连到t,流量为1
跑最小割就可以了
不难感受到这种建图的正确性

这题代码就先咕咕咕了

G.Check Pattern is Bad

做法其实很简单
先把一定要填的?,即填某种字符会出现不合法的?,先填了
如果不存在,就随便指定一个?为随便一种字符,然后重复上述过程即可
最后跑出来有解就是有解,没解就是没解
目前没有严格证明
当时再考场上可以大概感受到随便填一下基本上也是有解的
当时因为想了一个假填法,所以已经写完了bfs的过程,但后面卡在了填未知?的位置
赛中觉得是随机个几次,应该就能过
事实证明随机一次就是一定有解的。
但当时手里卡着别的题,榜上过的人也不多,我也没有严格证明就没敢冲了。还是有点可惜

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PI;
const int N=105;
char ss[N][N];
int n,m;
queue<PI> q;
int tx[4]={0,0,1,1};
int ty[4]={0,1,0,1};
bool check1 (int x,int y)
{
	if (x<=0||x>=n||y<=0||y>=m) return false;
	if (ss[x][y]=='B'&&ss[x+1][y]=='W'&&ss[x][y+1]=='W'&&ss[x+1][y+1]=='B') return false;
	if (ss[x][y]=='W'&&ss[x+1][y]=='B'&&ss[x][y+1]=='B'&&ss[x+1][y+1]=='W') return false;
	return true;
}
void check (int x,int y)
{
	if (x<=0||x>=n||y<=0||y>=m) return ;
	int xx=-1,yy=-1;
	for (int u=0;u<4;u++)
	{
		int nx=x+tx[u],ny=y+ty[u];
		if (ss[nx][ny]=='?')
		{
			if (xx!=-1) return ;
			xx=nx;yy=ny;
		}
	}
	if (xx==-1) return ;
	ss[xx][yy]='B';
	if (check1(x,y)==false) {ss[xx][yy]='W';q.push({xx,yy});return ;}
	ss[xx][yy]='W';
	if (check1(x,y)==false)	{ss[xx][yy]='B';q.push({xx,yy});return ;}
	ss[xx][yy]='?';
}
void solve ()
{
	while (!q.empty())
	{
		int x=q.front().first,y=q.front().second;q.pop();
		for (int u=-1;u<=1;u++)
		for (int i=-1;i<=1;i++)
		check(x+u,y+i);
	}
}
int main()
{
	int T;
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d%d",&n,&m);
		for (int u=1;u<=n;u++) scanf("%s",ss[u]+1);
		for (int u=1;u<n;u++)
		for (int i=1;i<m;i++)
		check(u,i);
		solve();
		for (int u=1;u<=n;u++)
		for (int i=1;i<=m;i++)
		if (ss[u][i]=='?')
		{
			ss[u][i]='B';
			q.push({u,i});
			solve();
		}
		bool tf=true;
		for (int u=1;u<n;u++)
		for (int i=1;i<m;i++)
		if (check1(u,i)==false)
		tf=false;
		if (!tf)	{printf("NO\n");continue;}
		printf("YES\n");
		for (int u=1;u<=n;u++)
		{
			for (int i=1;i<=m;i++)	printf("%c",ss[u][i]);
			printf("\n");
		}
	}
	return 0;
}

C.String-dle Count:

其实,最后每一种字符只有两种状态:
1.出现了x,此时就已经知道该字符有多少个了
2.没有出现x,那么相当于知道了这个字符至少有多少个记为\(L_I\)
同时,我们可以维护出每一个位置不可以填某个字符
考虑从左往右填,记录一个状态为填了前i个字符
由于有下界的限制,因此还需要每种字符已经出现了多少次
由于\(\sum L_i\le k\),因此可以简单地压成一个二进制数
至于具体出现了多少次的字符,只需要保证再出现够\(L_i\)个字符后不再转移即可
目前的写法是O(\(262^kk\))的,但其实可以写成\(O(2^kk)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MOD=1e9+7;
const int N=10005;
const int K=20;
int add (int x,int y){x=x+y;return x>=MOD?x-MOD:x;}
int mul (int x,int y){return (LL)x*y%MOD;}
int dec (int x,int y){x=x-y;return x<0?x+MOD:x;}
int Pow (int x,int y)
{
	int t=1;
	while (y)
	{
		if (y&1) t=mul(t,x);
		y>>=1;x=mul(x,x);
	}
	return t;
}
int L[30],R[30];
char s[K],t[K];
int n,m;
int cnt[30];
bool ban[K][30];
bool p[N];
void Init()
{
	scanf("%s%s",s,t);
	for (int u=0;u<26;u++) cnt[u]=0;
	for (int u=0;u<m;u++) if (t[u]=='O')
	{
		int x=s[u]-'A';
		cnt[x]++;
		for (int i=0;i<26;i++) if (i!=x) 	ban[u][i]=1;
	}
	for (int u=0;u<m;u++) if (t[u]!='O')
	{
		int x=s[u]-'A';
		ban[u][x]=1;
		if (t[u]=='-') cnt[x]++;
		else 		   {p[x]=false;R[x]=min(R[x],cnt[x]);}
	}
	for (int u=0;u<26;u++) L[u]=max(L[u],cnt[u]);
}
int mask[30];
int bel[30];
int f[K][1<<K];
int inv[30];
int main()
{
	inv[0]=1;for (int u=1;u<=20;u++) inv[u]=mul(inv[u-1],Pow(u,MOD-2));
	memset(ban,false,sizeof(ban));
	for (int u=0;u<26;u++) p[u]=true;
	scanf("%d%d",&n,&m);
	for (int u=0;u<26;u++) {L[u]=0;R[u]=m;}
	while (n--) Init();	
	
	for (int u=0;u<26;u++) if (L[u]>R[u]) {printf("0\n");return 0;}
	n=0;
	for (int u=0;u<26;u++)
	{
		mask[u]=0;
		for (int i=0;i<L[u];i++)
		{
			mask[u]|=(1<<n);bel[n]=u;n++;	
			if (n>m) {printf("0\n");return 0;}
		}
	}
	f[0][0]=1;
	for (int u=0;u<m;u++)
	for (int i=0;i<(1<<n);i++)
	{
		if (!f[u][i]) continue;
		for (int k=0;k<n;k++) if (!ban[u][bel[k]]&&!((i>>k)&1))	
		{
		
			f[u+1][i|(1<<k)]=add(f[u+1][i|(1<<k)],f[u][i]);
		}
		for (int k=0;k<26;k++) if (p[k]&&(mask[k]==(mask[k]&i))&&!ban[u][k]) 
		{
			f[u+1][i]=add(f[u+1][i],f[u][i]);
		}
	}
	int ans=f[m][(1<<n)-1];
	for (int u=0;u<26;u++) ans=mul(ans,inv[L[u]]);
	printf("%d\n",ans);
	return 0;
}
posted @ 2022-08-22 23:48  Als123  阅读(873)  评论(0编辑  收藏  举报