【中国剩余定理】【技巧】codeforces102056C Heretical … Möbius

Rikka was walking around the school building curiously until a strange room with a door number of 404 caught her eyes.

It seemed like a computer room — there were dozens of computers lying orderly, but papers, pens, and whiteboards everywhere built up a nervous atmosphere. Suddenly, Rikka found some mysterious codes displayed on a computer which seemed to have nothing different from others — is this a message from inner world?

Excited Rikka started her exploration. The message was generated by a program named for_patterns_in_mobius which outputted a string s of length 109, containing the value of |μ(x)| for x=1,2,…,109 in order.

Suddenly, Rikka heard footsteps outside. She quickly took a screenshot and left. The screenshot recorded a string t of length 200, perhaps a substring of s. Now Rikka wonders if it is really a substring of s, and if so, where it first appears in s.

Could you help her to decipher the codes?

Input
There are 10 lines in total. Each line contains 20 characters, each of which is either “0” or “1”. t is the concatenation of them — the result of concatenating them in order.

Output
Output a single integer in the only line. If t is a substring of s, output the first position it appears in s, that is, the minimum positive integer p such that all the digits |μ(p+i)| for i=0,1,…,199 form the string t. Otherwise output −1.


 出自今天EC-final的模拟。花在这题上不少时间,主要还是被卡常了。
 首先这个莫比乌斯函数的绝对值,显然如果是0,就意味着有平方因子,否则就没有。单纯枚举当然不行。注意到因为4的倍数一定是0,9的倍数一定是0……169的倍数一定是0,那么我们可以对4,9,25,49,121,169这6个200以内这质数平方进行考虑,求出起点位置对于每个数的模数可能是多少,然后进行枚举,用中国剩余定理求出可能的解。但是这些可能的解的数量很多该怎么办?事实上,经过直觉和程序检查,发现0的个数不会超过93个,那么超过93个直接输出-1就行了。剩余情况的话模数的可能性是不会很多的。
 最后对可能答案的200个数进行一一检查。一开始被卡常数了。主要是检查一个数是否有平方因子,我只能想到 s q r t ( x ) sqrt(x) sqrt(x)的做法,这样会超时。急中生智,我把拥有比较大的平方因子的数先筛出来,放进一个set,这样的话每次检查一个数x,先枚举小的平方因子,再在set里查找,效率就大大提升了。

#include
#include
#include
using namespace std;
using LL=long long;

const int sz=160;  //sz代表暴力枚举上限
const int m[6]={4,9,25,49,121,169};
const int p[169]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,
					53,59,61,67,71,73,79,83,89,97,
					101,103,107,109,113,127,131,137,139,149,
					151,157,163,167,173,179,181,191,193,197,199,
					211,223,227,229,233,239,241,
					251,257,263,269,271,277,281,283,293,
					307,311,313,317,331,337,347,349,
					353,359,367,373,379,383,389,397,
					401,409,419,421,431,433,439,443,449,
					457,461,463,467,479,487,491,499,
					503,509,521,523,541,547,
					557,563,569,571,577,587,593,599,
					601,607,613,617,619,631,641,643,647,
					653,659,661,673,677,683,691,
					701,709,719,727,733,739,743,
					751,757,761,769,773,787,797,
					809,811,821,823,827,829,839,
					853,857,859,863,877,881,883,887,
					907,911,919,929,937,941,947,
					953,967,971,977,983,991,997}; //1000以内素数表
vector<int> E[6];
char s[205];
int cnt,ans=2E9,a[6];
set<int> S;

bool check(int x, int y)
{
	for(int i=0;i*x+y<200;i++)
		if(s[i*x+y]=='1')
			return false;
	return true;
}

LL gcd(LL a, LL b, LL &d, LL &x, LL &y)
{
	if(!b)
		d=a,x=1,y=0;
	else
		gcd(b,a%b,d,y,x),y-=x*(a/b);
}

bool has_sq(int x) 
{
	for(int i=0;i<sz;i++)
		if(x%(p[i]*p[i])==0)  //素数表的作用
			return true;
	return S.count(x);
}

bool check_Res(int res)  //检查
{
	for(int i=0;i<200;i++)
		if((s[i]=='0')^has_sq(res+i))
			return false;
	return true;
}

int get_Ans()  //中国剩余定理求解部分
{
	LL M=1,d,y,x=0;
	for(int i=0;i<6;i++)
		M*=m[i];
	for(int i=0;i<6;i++)
	{
		LL w=M/m[i];
		gcd(m[i],w,d,d,y);
		x=(x+y*w*a[i])%M;
	}
	int res=(x+M)%M;
	if(check_Res(res))
		return res;
	if(res+M+200-1<=1E9&&check_Res(res+M))  //注意,一个模数可能对应有两个解!所以要检查res+M
		return res+M;
	return 2E9;
}

void dfs(int x)
{
	if(x==6)
	{
		ans=min(ans,get_Ans());
		return ;
	}
	for(int i:E[x])
		a[x]=i,dfs(x+1);
}

int main()
{
	for(int i=p[sz];i*i<=1E9;i++)
		for(int j=i*i;j<=1E9;j+=i*i)
			S.insert(j);
	for(int i=0;i<10;i++)
		scanf("%s",s+i*20);
	for(int i=0;i<200;i++)
		if(s[i]=='0')
			cnt++;
	if(cnt>93)
	{
		printf("-1");
		return 0;
	}
	for(int i=0;i<6;i++)
		for(int j=0;j<m[i];j++)
			if(check(m[i],j))  //寻找可能模数
				E[i].push_back((m[i]-j)%m[i]);
	dfs(0);
	printf("%d",ans==2E9?-1:ans);
	return 0;
}

 后来查到了一个更好的解决方法,枚举平方因子,然后看平方因子在这两百个数字中的倍数,(其实就是筛法啦)因为一个数最多是log级别个数的倍数,所以这样的枚举是 s q r t ( x ) ∗ l o g n sqrt(x)*logn sqrt(x)logn级别的。效率更棒。参考https://blog.csdn.net/a1214034447/article/details/86373308

你可能感兴趣的:(Codeforces,中国剩余定理)