【题解】CF722F Cyclic Cipher

题目大意:给定n个数列,第 i 个数列包含ki个不超过m的正整数,同一数列里的数互不相同。每一秒将n个数列中的数左移一个位置,每个数列第一个数则移到该数列最后,并在一张纸上记下每个数列的第一个数。10^100秒过后,对于所有的1<=x<=m,求x在纸上出现的最长的连续的一段长度,该段必须是同一秒中记下的数。
数据范围:1 ≤ n, m ≤ 100 000,1 ≤ ki ≤ 40,∑ki<=200 000

solution:
对于每个 i, 相当于在子区间统计答案,考虑双指针。注意到一组同余方程如果有解,那么对于其中任意两个一定满足 a1+k1m1=a2+k2m2 有解,即 gcd(m1,m2)|a2-a1 。又因为 m_i<=40 ,所以暴力判断每种 m_i 不会超过 40 。因为如果有两个相同的模数的话,一定满足 a_i=a_j 。每次新加入一个模数判断之前是否出现过。小优化:对于 f[t2[i]]==1 的情况将模数 1~40 判断,否则说明之前判断过,直接跳过。时间复杂度 O(40nlog40) 。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
#define PII pair<int,int>
#define rd(x) scanf("%lld",&(x))
using namespace std;
const int mx=1e5+5;
int n,Maxa,c[45],f[45];
int t1[mx],t2[mx],t3[mx],cnt;
ll a[mx],m[mx];
vector<PII> vec[mx];
//双指针 yyds  
ll gcd(ll x,ll y) {
	return y==0?x:gcd(y,x%y);
}
ll lcm(ll x,ll y) {
	if(x==0||y==0) return x^y;
	return x/gcd(x,y)*y;
}
ll fmul(ll x,ll y,ll z) {
	x%=z,y%=z; if(y<0) x=-x,y=-y;
	ll sum(0);
	for(;y;y>>=1) {
		if(y&1) sum=(sum+x)%z;
		x=(x+x)%z; 
	}
	return sum;
}
ll fpow(ll x,ll y,ll z) {
	x%=z;
	ll mul(1);
	for(;y;y>>=1) {
		if(y&1) mul=mul*x%z;
		x=x*x%z;
	}
	return mul;
}
ll inv(ll x,ll y) {
	if(y==1) return 0;
	return fpow(x,y-2,y);
}
void exgcd(ll &x,ll &y,ll a,ll b,ll &d) {
	if(b==0) {
		x=1,y=0,d=a;
	}
	else {
		exgcd(y,x,b,a%b,d);
		y-=x*(a/b);
	}
}
bool excrt() {
	for(int i=1;i<=n;i++) {
		if(a[i]>=m[i]) return 1;
	}
	for(int i=2;i<=n;i++) {
		ll tmp=((a[i]-a[1])%m[i]+m[i])%m[i];
		ll k1,k2,d; exgcd(k1,k2,m[1],m[i],d);
		if(tmp%d) return 1;
		k1=(k1%(m[i]/d)*(tmp/d)%(m[i]/d)+(m[i]/d))%(m[i]/d);
		ll M=m[1]/d*m[i];
		a[1]=(a[1]+m[1]*k1%M)%M;
		m[1]=M;
	}
	return 0;
}
ll solve(int id) {
	cnt=0;
	for(auto y:vec[id]) {
		t1[++cnt]=y.first,t2[cnt]=m[y.first],t3[cnt]=y.second;
	}
	int j=1,res=0;
	for(int i=1;i<=cnt;i++) {
		if(t1[i]!=t1[i-1]+1) {
			for(;j<i;f[t2[j]]--,j++);
		}
		if(f[t2[i]]&&c[t2[i]]!=t3[i]) {
			for(;j<i&&f[t2[i]];f[t2[j]]--,j++);
		}
		f[t2[i]]++,c[t2[i]]=t3[i];
		if(f[t2[i]]==1) {
			for(int k=1;k<=40;k++) {
				if(f[k]&&(t3[i]-c[k])%gcd(t2[i],k)) {
					for(;j<i&&f[k];f[t2[j]]--,j++);
				}
			}
		}
		res=max(res,i-j+1);
	}
	for(;j<=cnt;f[t2[j]]--,j++);
	return res;
}
int main() {
    scanf("%d%d",&n,&Maxa);
    for(int i=1;i<=n;i++) {
    	scanf("%lld",&m[i]);
    	for(int j=0;j<m[i];j++) {
    		int x; scanf("%d",&x);
    		vec[x].push_back(make_pair(i,j));
		}
	}
	for(int i=1;i<=Maxa;i++) {
		printf("%lld\n",solve(i));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值