题解 [SDOI2016]墙上的句子

题解 [SDOI2016]墙上的句子

题目链接

题目分析

发现数据范围很小,考虑使用网络流。

首先,对于回文串单词,无论正着读还是反着读都要对答案贡献一,所以先处理所有回文串单词,然后再删除这些回文串单词去考虑其他单词。有了回文串,那么一个单词的字典序要么严格大于它的反串要么严格小于它的反串。

将某个单词和它的反串一起考虑,如果这个单词和其反串都出现了,那么会对答案有 2 的贡献,这启发我们使用最小割,不妨假设这个单词的字典序严格小于它的反串,那么我们认为这个单词出现在了最终的字典中当且仅当源点 \(S\) 可以到达这个单词,其反串出现在了最终的字典中当且仅当这个单词可以到达汇点 \(T\) ,然后这个单词和其反串有一条流量为 2 的边,由此建图可以得到如果这个单词和其反串同时出现就必须要割掉这两个点之间的边,产生 2 的贡献。

按照上面的思路建出来的图大概是这样:

考虑如何用给定的矩阵去限制这些点和源点 \(S\) 汇点 \(T\) 之间的连通性。考虑到题目保证从某一个位置去看得到的单词要么字典序严格小于反串要么严格大于反串,所以某一行或者列从某个方向看相当于是让某些在上面的点和 \(S\) 连通或者是让某些在下面的点和 \(T\) 连通。

某一行要么从左往右看要么从右往左看,不妨假设从左往右看得到的单词的字典序都严格小于其反串,可以使用一个经典模型,给每个要求建一个节点,然后 \(S\) 直接和这个节点连边,这个节点和 \(T\) 直接连边,我们钦定如果这个要求和 \(S\) 的连边没有被割断,那么表示它从左往右看,否则表示它从右往左看,然后我们就可以根据这个钦定的方法去给要求节点和单词连边了,为了保证要求节点只会割断一边,可以给这个流量设置一个极大值 \(x\) ,假如一个要求没有确定究竟是从左看还是从右看,那么必然需要割掉一个位置,此时就要给答案额外减掉一个 \(x\)

举个例子吧,最后建出来的图大概是这样的:

解释一下这张图是什么意思:设 1,3,5 号节点分别表示单词 AB CD EF ,那么 2,4,6 号节点分别表示单词 BA DC FE ,那么 7 号节点从左往右看的单词就是 AB,EF , 8 号节点从左往右看的单词就是 CD , 9 号节点从左往右看的单词就是 AB ,其中 7 号节点要求必须从左往右看, 8 号节点要求必须从右往左看, 9 号节点任意,那么我们建出来的图就是上面的样子,可以看出来,上面的图只需要割断 9 和汇点 T 连着的边就行了。

参考代码

#include<set>
#include<map>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ch() getchar()
#define pc(x) putchar(x)
#define inf 10000
#define INF 1000000000
using namespace std;
template<typename T>void read(T&x){
	static char c;static int f;
	for(c=ch(),f=1;c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c>='0'&&c<='9';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>void write(T x){
	static char q[65];int cnt=0;
	if(x<0)pc('-'),x=-x;
	q[++cnt]=x%10,x/=10;
	while(x)
		q[++cnt]=x%10,x/=10;
	while(cnt)pc(q[cnt--]+'0');
}
const int maxn=10005,maxm=1000005;
struct Edge{
	int v,w,nt;
	Edge(int v=0,int w=0,int nt=0):
		v(v),w(w),nt(nt){}
}e[maxm];
int hd[maxn],num=1;
void qwq(int u,int v,int w){
	e[++num]=Edge(v,w,hd[u]),hd[u]=num;
}
void qvq(int u,int v,int w){
	qwq(u,v,w);qwq(v,u,0);
}
int tot,S=0,T=1,dis[maxn],q[maxn];
int bfs(void){
	memset(dis,0,(tot+1)*4);
	int fro=0,bac=0;dis[q[bac++]=S]=1;
	while(fro<bac){
		int u=q[fro++];
		for(int i=hd[u];i;i=e[i].nt){
			int v=e[i].v,w=e[i].w;
			if(w&&!dis[v])
				dis[q[bac++]=v]=dis[u]+1;
		}
	}
	return dis[T];
}
int cur[maxn];
int dfs(int u,int ep){
	if(u==T)return ep;int re=0;
	for(int&i=cur[u];i;i=e[i].nt){
		int v=e[i].v,w=e[i].w;
		if(w&&dis[v]==dis[u]+1){
			int tmp=dfs(v,min(ep,w));
			re+=tmp;ep-=tmp;
			e[i^1].w+=tmp;e[i].w-=tmp;
			if(!ep)break;
		}
	}
	return re;
}
int dinic(void){
	int re=0;
	while(bfs()){
		memcpy(cur,hd,(tot+1)*4);
		re+=dfs(S,INF);
	}
	return re;
}
string ReVerSe(string s){
	string re="";int len=s.length();
	for(int i=0;i<len;++i)re=s[i]+re;
	return re;
}
set<string>s;
map<string,int>sp;
int row[80],col[80],rvr[80],rvc[80];
char mp[80][80];
int main(){
	int test;read(test);
	while(test--){
		int n,m;
		read(n),read(m);
		for(int i=1;i<=n;++i)read(row[i]);
		for(int i=1;i<=m;++i)read(col[i]);
		for(int i=1;i<=n;++i){
			for(int j=1;j<=m;++j){
				char c=ch();
				while(c!='_'&&(c<'A'||c>'Z'))c=ch();
				mp[i][j]=c;
			}
		}
		s.clear();sp.clear();
		for(int i=1;i<=n;++i){
			rvr[i]=true;
			mp[i][m+1]='_';
			string str="";
			for(int j=1;j<=m+1;++j){
				if(mp[i][j]=='_'){
					if(str!=""){
						string rts=ReVerSe(str);rvr[i]&=(rts<=str);
						s.insert(str),s.insert(rts);
					}
					str="";
				}
				else str=str+mp[i][j];
			}
		}
		for(int j=1;j<=m;++j){
			rvc[j]=true;
			mp[n+1][j]='_';
			string str="";
			for(int i=1;i<=n+1;++i){
				if(mp[i][j]=='_'){
					if(str!=""){
						string rts=ReVerSe(str);rvc[j]&=(rts<=str);
						s.insert(str),s.insert(rts);
					}
					str="";
				}
				else str=str+mp[i][j];
			}
		}
		tot=1;int ans=0;
		memset(hd,0,sizeof hd);num=1;
		for(set<string>::iterator it=s.begin();it!=s.end();++it){
			string abc=*it,cba=ReVerSe(abc);
			if(abc==cba)++ans;
			else if(abc<cba&&s.find(cba)!=s.end()){
				sp[abc]=++tot;sp[cba]=++tot;
				qvq(tot-1,tot,2);
			}
		}
		for(int i=1;i<=n;++i){
			++tot;
			if(!row[i])/*continue*/;
			else if((row[i]==1)^(rvr[i]))
				qvq(S,tot,inf);
			else
				qvq(tot,T,inf);
			string str="";
			for(int j=1;j<=m+1;++j){
				if(mp[i][j]=='_'){
					if(str!=""&&sp.find(str)!=sp.end()){
						string rts=ReVerSe(str);if(rts<str)swap(rts,str);
						qvq(tot,sp[str],inf),qvq(sp[rts],tot,inf);
					}
					str="";
				}
				else str=str+mp[i][j];
			}
		}
		for(int j=1;j<=m;++j){
			++tot;
			if(!col[j])/*continue*/;
			else if((col[j]==1)^(rvc[j]))
				qvq(S,tot,inf);
			else
				qvq(tot,T,inf);
			string str="";
			for(int i=1;i<=n+1;++i){
				if(mp[i][j]=='_'){
					if(str!=""&&sp.find(str)!=sp.end()){
						string rts=ReVerSe(str);if(rts<str)swap(rts,str);
						qvq(tot,sp[str],inf),qvq(sp[rts],tot,inf);
					}
					str="";
				}
				else str=str+mp[i][j];
			}
		}
		write(dinic()+ans),pc('\n');
	}
	return 0;
}
posted @ 2020-10-23 15:15  xiaolilsq  阅读(193)  评论(0编辑  收藏  举报