【二分】【字符串哈希】【二分图最大匹配】【最大流】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem I. Minimum Prefix

给你n个字符串,问你最小的长度的前缀,使得每个字符串任意循环滑动之后,这些前缀都两两不同。

二分答案mid之后,将每个字符串长度为mid的循环子串都哈希出来,相当于对每个字符串,找一个与其他字符串所选定的子串不同的子串,是个二分图最大匹配的模型,可以匈牙利或者Dinic跑最大流看是否满流。

一个小优化是对于某个字符串,如果其所有不同的子串数量超过n,那么一定满足,可以直接删去。

卡常数,不能用set,map啥的,采取了用数组记录哈希值,排序后二分的手段进行去重和离散化。

#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
#define INF 2147483647
#define MAXN 200301
#define MAXM 800501
int v[MAXM],cap[MAXM],en,first[MAXN],next[MAXM];
int d[MAXN],cur[MAXN];
queue<int>q;
int n,S,T;
void Init_Dinic(){memset(first,-1,sizeof(first)); en=0;}
void AddEdge(const int &U,const int &V,const int &W)
{
    v[en]=V; cap[en]=W;
    next[en]=first[U]; first[U]=en++;
    v[en]=U; cap[en]=0;
    next[en]=first[V]; first[V]=en++;
}
bool bfs()
{
    memset(d,-1,sizeof(d)); q.push(S); d[S]=0;
    while(!q.empty())
      {
          int U=q.front(); q.pop();
          for(int i=first[U];i!=-1;i=next[i])
            if(d[v[i]]==-1 && cap[i])
              {
                d[v[i]]=d[U]+1;
                q.push(v[i]);
              }
      }
    return d[T]!=-1;
}
int dfs(int U,int a)
{
    if(U==T || !a) return a;
    int Flow=0,f;
    for(int &i=cur[U];i!=-1;i=next[i])
      if(d[U]+1==d[v[i]] && (f=dfs(v[i],min(a,cap[i]))))
        {
          cap[i]-=f; cap[i^1]+=f;
          Flow+=f; a-=f; if(!a) break;
        }
    if(!Flow) d[U]=-1;
    return Flow;
}
int max_flow()
{
    int Flow=0,tmp=0;
    while(bfs())
      {
          memcpy(cur,first,sizeof(first));
          while(tmp=dfs(S,INF)) Flow+=tmp;
      }
    return Flow;
}
typedef unsigned long long ull;
const ull base=107;
ull bs[200005],hss[200005],hss2[200005];
char* a[205];
char b[200005];
int tmphss[200005];
bool neednot[205];
int len[205],pps[205],ppsend[205];
bool check(int x){
	int pp=0;
	Init_Dinic();
	memset(neednot,0,sizeof(neednot));
	int N=n;
	for(int i=1;i<=n;++i){
		int last=pp;
		pps[i]=last+1;
		int FirstPre=min(len[i],x);
		ull hs=0;
		for(int j=0;j<FirstPre;++j){
			hs=hs*base+(ull)a[i][j];
		}
		hss[++pp]=hs;
		for(int j=FirstPre;j<len[i];++j){
			hs-=(bs[x-1]*(ull)a[i][j-x]);
			hs=hs*base+(ull)a[i][j];
			hss[++pp]=hs;
		}
		for(int j=0;j<FirstPre-1;++j){
			hs-=(bs[FirstPre-1]*(ull)a[i][len[i]-FirstPre+j]);
			hs=hs*base+(ull)a[i][j];
			hss[++pp]=hs;
		}
		sort(hss+last+1,hss+pp+1);
		int Size=0;
		for(int j=last+2;j<=pp;++j){
			if(hss[j]!=hss[j-1]){
				++Size;
			}
		}
		if(Size>n){
			--N;
			neednot[i]=1;
		}
		ppsend[i]=pp;
	}
	for(int i=1;i<=pp;++i){
		hss2[i]=hss[i];
	}
	sort(hss2+1,hss2+pp+1);
	S=n+pp+1;
	T=n+pp+2;
	for(int i=1;i<=n;++i){
		if(!neednot[i]){
			AddEdge(S,i,1);
			for(int j=pps[i];j<=ppsend[i];++j){
				if(j==pps[i] || hss[j]!=hss[j-1]){
					AddEdge(i,n+lower_bound(hss2+1,hss2+pp+1,hss[j])-hss2,1);
				}
			}
		}
	}
	for(int i=1;i<=pp;++i){
		if(i==1 || hss2[i]!=hss2[i-1]){
			AddEdge(i+n,T,1);
		}
	}
	return max_flow()>=N;
}
int main(){
//	freopen("i.in","r",stdin);
	bs[0]=1;
	for(int i=1;i<=200000;++i){
		bs[i]=bs[i-1]*base;
	}
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%s",b);
		len[i]=strlen(b);
		a[i]=new char[len[i]+1];
		for(int j=0;j<len[i];++j){
			a[i][j]=b[j];
		}
	}
	int l=1,r=*max_element(len+1,len+n+1);
	while(l<r){
		int mid=(l+r>>1);
		if(check(mid)){
			r=mid;
		}
		else{
			l=mid+1;
		}
	}
	printf("%d\n",l);
	return 0;
}
时间: 2024-10-18 13:40:11

【二分】【字符串哈希】【二分图最大匹配】【最大流】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem I. Minimum Prefix的相关文章

【二分图】【并查集】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem L. Canonical duel

给你一个网格(n<=2000,m<=2000),有一些炸弹,你可以选择一个空的位置,再放一个炸弹并将其引爆,一个炸弹爆炸后,其所在行和列的所有炸弹都会爆炸,连锁反应. 问你所能引爆的最多炸弹数. 转化成: 将行列当成点,炸弹当成边,然后你可以给这个二分图加1条边,问你最大的连通块的边的数量. 可以通过枚举所有可以建的边,通过并查集来尝试更新答案.由于一条边必然会让总度数+2,所以一个连通块的边数是所有点的度数之和/2. 并查集不必要动态维护集合的大小,一开始就建好并查集,提前统计好即可. 最后

UVa1663 Purifying Machine (二分图最大匹配,最大流)

链接:http://vjudge.net/problem/UVA-1663 分析:先把原来m个模板串取出来扔进set里去重,然后将去重后的匹配串从1~cnt编号.题目说每个模板串最多一个星号“*”,所以说一个匹配串只有唯一的一个跟它只有一个位上的数字不同的另一个匹配串组合成一个模板串,这就成了二分图最大匹配问题了.然后针对每个匹配串找出和它只有一位数字不同的其它匹配串,把其编号存进数组vector<int> e[i]里.然后对cnt个匹配串以e[i][j]为状态转移进行dfs黑白染色,这样就把

它处资料:二分图最大匹配的匈牙利算法

资料出处:点击打开链接 匈牙利算法 二分图最大匹配的匈牙利算法:  二分图是这样一个图,它的顶点能够分类两个集合X和Y,全部的边关联在两个顶点中.恰好一个属于集合X.还有一个属于集合Y. 最大匹配: 图中包括边数最多的匹配称为图的最大匹配. 完美匹配: 假设全部点都在匹配边上.称这个最大匹配是完美匹配. 最小覆盖: 最小覆盖要求用最少的点(X集合或Y集合的都行)让每条边都至少和当中一个点关联.能够证明:最少的点(即覆盖数)=最大匹配数 最小路径覆盖: 用尽量少的不相交简单路径覆盖有向无环图G的全

Ural1109_Conference(二分图最大匹配/匈牙利算法/网络最大流)

解题报告 二分图第一题. 题目描述: 为了参加即将召开的会议,A国派出M位代表,B国派出N位代表,(N,M<=1000) 会议召开前,选出K队代表,每对代表必须一个是A国的,一个是B国的; 要求每一个代表要与另一方的一个代表联系,除了可以直接联系,也可以电话联系,求电话联系最少 思路: 电话联系最少就要使直接联系最大,又是一一匹配关系,就是二分图的最大匹配. 下面是匈牙利算法. #include <cstdio> #include <cstring> #include <

SPOJ AMR12A The Black Riders --二分+二分图最大匹配

题意:有n个人,m个洞.每个洞能容纳一个人,每个人到每个洞需要花费一些时间.每个人到达一个洞后可以花C的时间来挖一个洞,并且最多挖一个洞,这样又能多容纳一人.求能使至少K个人进洞的最短时间. 解法:看到n个人和m个洞,并且人要进洞容易想到二分匹配,又是求极值的问题,应该是最大匹配.由于直接求极值不好求,可以将求极值问题转化为判定问题,即二分最短时间,然后判定能否达到.判定时,如果i到j的时间小于等于mid,就将i和j连一条边,如果T[i][j]+C <= mid 说明还来得及挖洞,将i和j+m连

【BZOJ4443】[Scoi2015]小凸玩矩阵 二分+二分图最大匹配

[BZOJ4443][Scoi2015]小凸玩矩阵 Description 小凸和小方是好朋友,小方给小凸一个N*M(N<=M)的矩阵A,要求小秃从其中选出N个数,其中任意两个数字不能在同一行或同一列,现小凸想知道选出来的N个数中第K大的数字的最小值是多少. Input 第一行给出三个整数N,M,K 接下来N行,每行M个数字,用来描述这个矩阵 Output 如题 Sample Input 3 4 2 1 5 6 6 8 3 4 3 6 8 6 3 Sample Output 3 HINT 1<

POJ 2289--Jamie&#39;s Contact Groups【二分图多重匹配问题 &amp;&amp;二分查找最大值的最小化 &amp;&amp; 最大流】

Jamie's Contact Groups Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 6902   Accepted: 2261 Description Jamie is a very popular girl and has quite a lot of friends, so she always keeps a very long contact list in her cell phone. The con

POJ1698_Alice&#39;s Chance(二分图多重最大匹配/最大流)

解题报告 http://blog.csdn.net/juncoder/article/details/38237641 题目传送门 题意: N个电影,每个电影在每一周有固定拍映时间,电影必须在W周前演完.有一个演员,他每天只能演一场电影,对于每部电影必须演完D天才算完. 思路: 二分图多重最大匹配问题,对于每个电影,源点与每个电影连上一条边容量为D,电影与每周7天对应拍映连线,容量为1,每周每天与汇点连线容量为1 在二分图最大匹配中,每个点(不管是X方点还是Y方点)最多只能和一条匹配边相关联,然

【CodeForces】961 F. k-substrings 字符串哈希+二分

[题目]F. k-substrings [题意]给定长度为n的串S,对于S的每个k-子串$s_ks_{k+1}...s_{n-k+1},k\in[1,\left \lceil \frac{n}{2} \right \rceil]$,找到满足[奇数长度][严格子串][同时是前缀和后缀]的最长子串.n<=10^6. [算法]字符串哈希+二分 [题解]任意两个对应子串,它们有一个不变量--它们的中心一定是i和n-i+1.而且固定中心之后,能延伸的最长相等子串是可以二分+哈希得到的. 所以枚举k,二分+