51Nod1317 相似字符串对 容斥原理 动态规划

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

题目传送门 - 51Nod1317

题意

  称一对字符串(A,B)是相似的,当且仅当满足以下条件:

  (1)字符串A和B都恰好包含N个字符;

  (2)A和B串中的每个字符都是小写字母的前k个字符,即A、B中只可能出现'a','b','c',...,('a'+k-1)这k个字符;

  (3)存在一个字符串C,满足:A+C=C+B。这里的“+”号表示字符串间的链接,即str1+str2 = str1str2,如:“aaa”+“csd”=“aaacsd”。

  现在给出N与k,问有多少种不同的相似字符串对,输出这个结果 mod 1,000,000,007的值。

  说明:两个字符串对(A,B)与(C,D)是不同的,只要 A!=C 或 B!=D。

  $nleq 10^9$

题解

  显然满足 $A+C=C+B$ 的 $A$ 和 $B$ 循环同构。

  于是原问题变成了有多少对串循环同构。

  我们设 $dp_i$ 表示长度为 $i$ 的 不存在长度小于 $i$ 的循环节的 字符串个数。

  这里的循环节是指满足把该循环节串重复多次可以组成原串的子串。

  显然 $dp_i=k^i -sum_limits{j|i} dp_j$ 。

  答案为 $sum_limits{i|n} i imes dp_i$ ,因为任意一个循环节长度为 $i$ 的串都可以选择 $i$ 个串与它循环同构。

  由于 $n$ 的因数特别少,因数最多的情况下, $n=735134400$ ,因数个数为 1344 。(我搜了1分钟才搜出来……)

  所以我们可以把 $n$ 的因数搞出来,设因数个数为  $m$ ,然后 $O(m^2)$ DP 解决本问题。

代码

#include <bits/stdc++.h>
using namespace std;
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;
}
const int N=3005,mod=1e9+7;
int n,k,v[N],m,dp[N];
int Pow(int x,int y){
	int ans=1;
	for (;y;y>>=1,x=1LL*x*x%mod)
		if (y&1)
			ans=1LL*ans*x%mod;
	return ans;
}
int main(){
	n=read(),k=read(),m=0;
	for (int i=1;i*i<=n;i++)
		if (n%i==0){
			v[++m]=i;
			if (i*i!=n)
				v[++m]=n/i;
		}
	sort(v+1,v+m+1);
	int ans=0;
	for (int i=1;i<=m;i++){
		dp[i]=Pow(k,v[i]);
		for (int j=1;j<i;j++)
			if (v[i]%v[j]==0)
				dp[i]=(dp[i]-dp[j]+mod)%mod;
		ans=(1LL*dp[i]*v[i]+ans)%mod;
	}
	ans=(ans+mod)%mod;
	printf("%d
",ans);
	return 0;
}

  

原文地址:https://www.cnblogs.com/zhouzhendong/p/51Nod1317.html