The 15th Jilin Provincial Collegiate Programming Contest

F. Cooking

题意:
给定若干 01 01 01串,求它们的最长公共前后缀长度之和。
思路:
多模式串匹配,首先考虑 A C AC AC自动机
如果我们不考虑最长的限制,只需要对所有的字符串从结尾开始跳 f a i l fail fail,跳到的点一定是一个公共前后缀。于是我们每跳到一个点会产生贡献 s i ∗ l e n i s_i*len_i sileni,其中 s i s_i si是节点 i i i出现的次数, l e n i len_i leni是这个位置代表的子串长度,最终答案就是 ∑ s i ∗ l e n i \sum s_i*len_i sileni
现在我们加上最长的限制,于是 f a i l fail fail链上不再是所有的点都满足要求了,现在我们只想要每个字符串上在 f a i l fail fail树上深度最深的那个点,这显然是很难做到的。
每个点在 f a i l fail fail树上的对应的最深的点为 m i m_i mi,其实也就是该字串的最大 b o r d o r bordor bordor,对所有的字符串都做一次 k m p kmp kmp我们就可以得到所有的 b o r d o r bordor bordor
l e n i len_i leni重新定义为点 i i i到点 m i m_i mi距离的差值,这样就相当于将原本一段的长度,划分成了多段,因为字符串的一个 b o r d o r bordor bordor一定会在 f a i l fail fail上出现,所以对答案没有影响

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e6+7;
char s[105];
int trie[maxn][10],fail[maxn],cnt,son[maxn],root[maxn],Next[105],w[maxn],ans;
void getnext(char m[])
{
    Next[0]=-1;
    int k=-1,j=0;
	int l=strlen(m);
    while(j<l)
    {
        if(k==-1||m[k]==m[j])
        {
            k++;j++;
            Next[j]=k;
        }else
        k=Next[k];
    }
}
int insert(char s[])
{
	int p=0,l=strlen(s);
	for(int i=0;i<l;i++)
	{
		int u=s[i]-'0';
		if(!trie[p][u])
		{
			trie[p][u]=++cnt;
		}
		p=trie[p][u];
		w[p]=i-Next[i+1]+1;
		son[p]++;
	}
	return p;
}
void make_fail()
{
	queue<int>q;
	for(int i=0;i<10;i++)
		if(trie[0][i])
			q.push(trie[0][i]);
	while(!q.empty())
	{
		int t=q.front();
		q.pop();
		for(int i=0;i<10;i++)
		{
			int p=trie[t][i];
			if(!p) trie[t][i]=trie[fail[t]][i];
			else
			{
				fail[p]=trie[fail[t]][i];
				q.push(p);
			}
		}
	}
}
void dfs(int u)
{
	if(u==0) return;
	ans+=son[u]*w[u];
	u=fail[u];
	dfs(u);
}
signed main()
{
	int n;
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s);
		getnext(s);
		Next[0]=0;
		root[i]=insert(s);
	}
	make_fail();
	for(int i=1;i<=n;i++)
	{
		dfs(root[i]);
	}
	printf("%lld\n",ans);
}

G. Matrix Repair

题意:
类似数独游戏,给定一个01矩阵和该矩阵每行每列的异或和。现在矩阵中部分值缺失,询问能否唯一复原该矩阵。
题解:
为了使解唯一,那么填的数字自然也就是每行/每列恰好只缺一个空的数字,十分显然的想到拓扑排序

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int maxn=2e5+7;
int n,a[1005][1005],l[1005],c[1005],nl[1005],nc[1005];
int xorl[1005],xorc[1005],call[1005],calc[1005];
int vis[1005][1005];
int main()
{
	scanf("%d",&n);
	int cnt=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			scanf("%d",&a[i][j]);
			if(a[i][j]==-1) continue;
			cnt++;
			nl[i]++;nc[j]++;
			call[i]^=a[i][j];calc[j]^=a[i][j];
		}
	}
	queue<PII>q;
	for(int i=1;i<=n;i++) scanf("%d",&xorl[i]);
	for(int i=1;i<=n;i++) scanf("%d",&xorc[i]);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if((nl[i]==n-1||nc[j]==n-1)&&a[i][j]==-1)
			q.push({i,j}),vis[i][j]=1;
		}
	}
	while(!q.empty())
	{
		auto [x,y]=q.front();
		q.pop();
		cnt++;
		if(nl[x]==n-1) a[x][y]=call[x]^xorl[x];
		else a[x][y]=calc[y]^xorc[y];
		call[x]=call[x]^a[x][y];calc[y]=calc[y]^a[x][y];
		nl[x]++;nc[y]++;
		if(nl[x]==n-1)
		{
			for(int i=1;i<=n;i++)
				if(a[x][i]==-1 && vis[x][i]==0) q.push({x,i}),vis[x][i]=1;
		}
		if(nc[y]==n-1)
		{
			for(int i=1;i<=n;i++)
				if(a[i][y]==-1 && vis[i][y]==0) q.push({i,y}),vis[i][y]=1;
		}
	}
	if(cnt==n*n)
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
				printf("%d ",a[i][j]);
			puts("");
		}
	else
		puts("-1");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值