• Codeforces 1009G Allowed Letters FMT,二分图,二分图匹配,霍尔定理


    原文链接https://www.cnblogs.com/zhouzhendong/p/CF1009G.html

    题目传送门 - CF1009G

    题意

      给定一个长度为 $n$ 的字符串 $s$ 。并给定 $m$ 条限制,第 $i$ 条限制声明了第 $i$ 个位置的字符可以取的值。如果没有声明表示可以任意取值。

      求一个字符串 $s$ 的排列,在满足 $m$ 条限制的同时,使得字典序最小。如果不存在满足限制条件的字符串,则输出 $-1$。

      $n,mleq 10^5$,字符集 $ = {'a','b','c','d','e','f'}$

    题解

      我们先考虑如何判定是否有解。

      统计一下原字符串中每一个字母的出现次数。容易建出一个二分图,左侧的 $n$ 个节点为字符串的每一个位置,右侧的 $n$ 个节点为 $n$ 个字母,这 $n$ 个字母中每种字符的出现次数等于原字符串中对应字符的出现次数;对于左侧的每一个点,即字符串的每一个位置,向它所能填的字母连上边(补充一下:这样做,右侧相同字母节点其实是等价的,他们的边集是相同的,这里设由字符 $c$ 组成的等价类为 $S_c$ )。那么,只需要求出最大匹配数,就可以知道最多有多少个位置可以放正确的字母。

      然而,我们是否可以得到一种较快的判定是否有完美匹配的做法呢?显然有啊。

      霍尔定理:设一个二分图 G 中的两部分顶点组成的集合分别为 X, Y ,则 G 中有一组无公共点的边,一端恰好为组成X的点的充分必要条件是:X中的任意k个点至少与Y中的k个点相邻。

      根据霍尔定理,得到:

      命题1 :一个满足 |X| = |Y| 的二分图,存在完美匹配的充分必要条件是:在 X 中任选 k 个点,至少与 Y 中 k 个点相邻。

      回到原图的判定。我们令左侧点集为 $Y$ ,右侧点集为 $X$ ,则我们只需要证明:在 $X$ 中任选 $k$ 个点,至少与 $Y$ 中 $k$ 个点相邻。但是我们显然不能去枚举子集。注意之前提到的: $X$ 中有等价的点,我们接下来称一组等价的点为一个等价类。考虑在 $X$ 中选择一些等价类,得到等价类集合 $E$,假设这些等价类包含了 $k$ 个节点,如果 $Y$ 中只存在小于 $k$ 个点与之相邻,那么根据命题1,原图不存在完美匹配;否则,令 $f(E)$ 表示等价类集合 $E$ 中的节点在 $Y$ 中的相邻点个数,对于每一个等价类 $S_cin E,cin{'a','b','c','d','e','f'}$ ,$forall S_csubseteq P_c,P_c eq emptyset $,则如果令 $Q = {P_c|S_cin E}$ 必然存在 $f(E)=f(P)$,从而 $sum_limits{P_cin Q}|P_c|leq sum _limits{S_cin E}|S_c|leq f(E)=f(P)$ ,故我们只需要检验所有等价类集合就可以了。

      由于字符集非常的小,而不同的等价类数是不大于字符集大小的,所以单次检验的运算次数为 $2^6$ 。我们发现这个速度非常快!所以我们可以考虑预处理每一个字符集的在每一个后缀限制中出现的非空子集个数,用到 FMT 来快速处理(不用会慢一点)。然后贪心的从字符串开头到结尾逐位按照字典序试着放字符,并判断快速判断是否合法即可。

      设字符集大小为 $x$ ,则时间复杂度为 $O(nxcdot 2^x)$ 。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=100005;
    int read(){
    	int x=0;
    	char ch=getchar();
    	while (!isdigit(ch))
    		ch=getchar();
    	while (isdigit(ch))
    		x=(x<<1)+(x<<3)+ch-48,ch=getchar();
    	return x;
    }
    char s[N];
    int n,v[N],suf[N][64],tot[64],sum[64],Log[64];
    void getv(){
    	int m=read();
    	memset(v,0,sizeof v);
    	while (m--){
    		int id=read();
    		char s[10];
    		scanf("%s",s+1);
    		int len=strlen(s+1);
    		for (int i=1;i<=len;i++)
    			v[id]|=1<<(s[i]-'a');
    	}
    	for (int i=1;i<=n;i++)
    		if (v[i]==0)
    			v[i]=63;
    }
    bool check(int p){
    	sum[0]=0;
    	for (int i=1;i<64;i++){
    		sum[i]=sum[i^(i&-i)]+tot[i&-i];
    		if (sum[i]<suf[p][i])
    			return 0;
    	}
    	return 1;
    }
    int main(){
    	Log[1]=0;
    	for (int i=2;i<64;i++)
    		Log[i]=Log[i>>1]+1;
    	gets(s+1);
    	n=strlen(s+1);
    	getv();
    	memset(tot,0,sizeof tot);
    	for (int i=1;i<=n;i++)
    		tot[1<<(s[i]-'a')]++;
    	memset(suf,0,sizeof suf);
    	for (int i=n;i>=1;i--){
    		for (int j=0;j<64;j++)
    			suf[i][j]=suf[i+1][j];
    		suf[i][v[i]]++;
    	}
    	for (int id=1;id<=n;id++)
    		for (int i=1;i<64;i<<=1)
    			for (int j=0;j<64;j++)
    				if (j&i)
    					suf[id][j]+=suf[id][j^i];
    	if (!check(1)){
    		printf("Impossible");
    		return 0;
    	}
    	for (int i=1;i<=n;i++)
    		for (int j=v[i],k=j&-j;j;j^=k,k=j&-j){
    			tot[k]--;
    			if (check(i+1)){
    				putchar('a'+Log[k]);
    				break;
    			}
    			tot[k]++;
    		}
    	return 0;
    }
    

      

  • 相关阅读:
    OKHttp使用详解
    spring okhttp3
    HTTPS SSL/TLS协议
    springboot @scheduled 并发
    spring @Scheduled 并发
    CORSFilter 跨域资源访问
    定时任务 spring @Scheduled注解
    spring proxy-target-class
    iOS 适用于Pad上的菜单弹出界面-最简单的一种实现记录
    Mac系统清理、占用空间大、空间不够、查看系统文件大小分布
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/CF1009G.html
Copyright © 2020-2023  润新知