codeforces 722F. Cyclic Cipher
2016-10-27 23:38
169 查看
题目链接:http://codeforces.com/problemset/problem/722/F
题目大意:给定n个数列,第 i 个数列包含ki个不超过m的正整数,同一数列里的数互不相同。每一秒将n个数列中的数左移一个位置,每个数列第一个数则移到该数列最后,并在一张纸上记下每个数列的第一个数。10^100秒过后,对于所有的1<=x<=m,求x在纸上出现的最长的连续的一段长度,该段必须是同一秒中记下的数。
数据范围:1 ≤ n, m ≤ 100 000,1 ≤ ki ≤ 40,∑ki<=200 000
题解:由于题目所求的是一段连续的区间,所以对于一个数x,如果它在一段连续的数列出现,并且找到一个时刻使得它们同时被写下,那么这就是一段连续的x。
所以我们的任务是如何判断它们能否在同一秒被写下。
1.exgcd+倍增预处理
对于第 i 个数列的第 bi 个数(bi∈[0,ki)),如果它在第t秒被记下,那么t应满足t=ki*x+bi。所以我们应该判断的是能否找到一个时刻t能同时表示成k1*x1+b1以及k2*x2+b2的形式,即能否找到x1,x2使得k1*x1+b1=k2*x2+b2,移项k1*x1-k2*x2=b2-b1,这就是一个不定方程,可以用扩展欧几里得求解,然后利用中国剩余定理将两个表达式合并成一个新的表达式k3*x+b3。于是方法就出来了,用倍增预处理出u[i][j]表示从i开始能否合并到i+2^j-1,然后我们枚举左端点,判断能达到的最远的右端点即可。
时间复杂度O(nlognlogLCM)
2.gcd+左右指针
事实上本题还有一个更简便的判断方法。我们知道对于两个表达式k1*x+b1和k2*x+b2,如果(b1-b2) mod gcd(k1,k2)=0,那么方程k1*x1-k2*x2=b2-b1就一定有解。所以判断一段连续数列的表达式可不可行只要判断它们是不是两两有解就可以了。我们知道,当k1=k2时,b1必须等于b2,而k<=40,所以我们判断一个新的表达式是否与前面的表达式冲突(即无解),只要判断不超过40次就可以了。于是我们记d[ i ]表示当k=i时b的值,f[ i ]表示个数,每次判断一个新的k和b,只要判断当f[ i ]>0时(d[ i ]-b) mod gcd(k,i)是否为0。设置两个左右指针 l 和 r ,每次对于当前的 l ,把 r 尽量往右移直到冲突为止。同时把没有冲突的k和b加入到相应的 f 和 d 中,l+1时只要把 f[k[l]] 减1就可以了。
时间复杂度O(40nlog40)
代码如下:
1.exgcd+倍增预处理
2.gcd+左右指针
题目大意:给定n个数列,第 i 个数列包含ki个不超过m的正整数,同一数列里的数互不相同。每一秒将n个数列中的数左移一个位置,每个数列第一个数则移到该数列最后,并在一张纸上记下每个数列的第一个数。10^100秒过后,对于所有的1<=x<=m,求x在纸上出现的最长的连续的一段长度,该段必须是同一秒中记下的数。
数据范围:1 ≤ n, m ≤ 100 000,1 ≤ ki ≤ 40,∑ki<=200 000
题解:由于题目所求的是一段连续的区间,所以对于一个数x,如果它在一段连续的数列出现,并且找到一个时刻使得它们同时被写下,那么这就是一段连续的x。
所以我们的任务是如何判断它们能否在同一秒被写下。
1.exgcd+倍增预处理
对于第 i 个数列的第 bi 个数(bi∈[0,ki)),如果它在第t秒被记下,那么t应满足t=ki*x+bi。所以我们应该判断的是能否找到一个时刻t能同时表示成k1*x1+b1以及k2*x2+b2的形式,即能否找到x1,x2使得k1*x1+b1=k2*x2+b2,移项k1*x1-k2*x2=b2-b1,这就是一个不定方程,可以用扩展欧几里得求解,然后利用中国剩余定理将两个表达式合并成一个新的表达式k3*x+b3。于是方法就出来了,用倍增预处理出u[i][j]表示从i开始能否合并到i+2^j-1,然后我们枚举左端点,判断能达到的最远的右端点即可。
时间复杂度O(nlognlogLCM)
2.gcd+左右指针
事实上本题还有一个更简便的判断方法。我们知道对于两个表达式k1*x+b1和k2*x+b2,如果(b1-b2) mod gcd(k1,k2)=0,那么方程k1*x1-k2*x2=b2-b1就一定有解。所以判断一段连续数列的表达式可不可行只要判断它们是不是两两有解就可以了。我们知道,当k1=k2时,b1必须等于b2,而k<=40,所以我们判断一个新的表达式是否与前面的表达式冲突(即无解),只要判断不超过40次就可以了。于是我们记d[ i ]表示当k=i时b的值,f[ i ]表示个数,每次判断一个新的k和b,只要判断当f[ i ]>0时(d[ i ]-b) mod gcd(k,i)是否为0。设置两个左右指针 l 和 r ,每次对于当前的 l ,把 r 尽量往右移直到冲突为止。同时把没有冲突的k和b加入到相应的 f 和 d 中,l+1时只要把 f[k[l]] 减1就可以了。
时间复杂度O(40nlog40)
代码如下:
1.exgcd+倍增预处理
#include <algorithm> #include <cstdio> typedef long long ll; int k[100005],a[200005],b[200005],ne[200005],fi[100005], c[100005],u[200005][20],tot=0,ans; ll f[100005][20],g[100005][20],x0,y0,d; void add(int x,int y,int z) { a[++tot]=y;b[tot]=z;ne[tot]=fi[x];fi[x]=tot; } void exgcd(ll a,ll b,ll c) { if (!b) { if (c%a) d=-1; else d=a; x0=c/a;y0=0; return; } exgcd(b,a%b,c); ll t=x0;x0=y0;y0=t-a/b*y0; } void solve(int l,int r) { for (int i=l;i<=r;i++) f[i][0]=k[a[c[i]]],g[i][0]=b[c[i]],u[i][0]=1; for (int i=r,j;i>=l;i--) { for (j=1;i+(1<<j)-1<=r && u[i+(1<<(j-1))][j-1];j++) { ll a0=f[i][j-1],b0=f[i+(1<<(j-1))][j-1], c0=g[i+(1<<(j-1))][j-1]-g[i][j-1]; exgcd(a0,b0,c0); if (d==-1) break; b0/=d;x0=(x0%b0+b0)%b0; f[i][j]=b0*a0; g[i][j]=(x0*a0+g[i][j-1])%f[i][j]; u[i][j]=1; } for (;i+(1<<j)-1<=r;j++) u[i][j]=0; } for (int i=l;i<=r;i++) { int j=0,x=0; for (;i+(1<<j)-1<=r && u[i][j];j++); j--;x=(1<<j);int y=i+(1<<j);ll F=f[i][j],G=g[i][j]; for (;y<=r;y+=1<<j,x+=1<<j) { for (;y+(1<<j)-1> 4000 ;r || !u[y][j];j--); for (;j>=0;j--) { ll a0=F,b0=f[y][j],c0=g[y][j]-G; exgcd(a0,b0,c0); if (d==-1) continue; b0/=d;x0=(x0%b0+b0)%b0; F=b0*a0; G=(x0*a0+G)%F; break; } if (j<0) break; } ans=std::max(ans,x); } } int main() { int n,m; scanf("%d%d\n",&n,&m); for (int i=1;i<=n;i++) { scanf("%d",&k[i]); for (int j=0;j<k[i];j++) { int x; scanf("%d",&x); add(x,i,j); } } for (int i=1;i<=m;i++) { int len=0;ans=0; for (int j=fi[i];j;j=ne[j]) c[++len]=j; for (int j=1,k;j<=len;j=k) { for (k=j+1;k<=len && a[c[k-1]]-1==a[c[k]];k++); solve(j,k-1); } printf("%d\n",ans); } }
2.gcd+左右指针
#include <algorithm> #include <cstdio> int k[100005],a[200005],b[200005],ne[200005],fi[100005], c[100005],d[50],f[50],tot=0; void add(int x,int y,int z) { a[++tot]=y;b[tot]=z;ne[tot]=fi[x];fi[x]=tot; } int gcd(int x,int y) { return y?gcd(y,x%y):x; } bool check(int k,int x) { for (int i=1;i<=40;i++) if (f[i] && (d[i]-x)%gcd(k,i)) return 0; d[k]=x;f[k]++; return 1; } int main() { int n,m; scanf("%d%d\n",&n,&m); for (int i=1;i<=n;i++) { scanf("%d",&k[i]); for (int j=0;j<k[i];j++) { int x; scanf("%d",&x); add(x,i,j); } } for (int i=1;i<=m;i++) { int len=0,ans=0; for (int j=fi[i];j;j=ne[j]) c[++len]=j; for (int j=1;j<=40;j++) f[j]=0; for (int l=1,r=1;r<=len;l++) { for (;r<=len && a[c[l]]-a[c[r]]==r-l;r++) if (!check(k[a[c[r]]],b[c[r]])) break; ans=std::max(ans,r-l); f[k[a[c[l]]]]--; } printf("%d\n",ans); } }
相关文章推荐
- Codeforces 515D - Drazil and Tiles (拓扑排序)
- Codeforces--626B--Cards(模拟)
- codeforces 484E Sign on Fence 可持久化线段树+二分查找
- C - Matrix CodeForces - 365C 思维
- Codeforces 677D Vanya and Treasure BFS+DP (分段)
- 【CodeForces】CodeForces Round #460 (Div. 2) 题解
- Tanya and Password - CodeForces 508 D 欧拉路径
- CodeForces 635A-Orchestra【图形规划】
- codeforces 702A 水 最长连续上什子序列
- Codeforces 706B Interesting drink
- Codeforces 392-C Yet Another Number Sequence (矩阵快速幂)
- CodeForces-768B Code for 1 二分搜索线段
- 第二十八次codeforces竞技结束 #291 Div 2
- CodeForces 633A-Ebony and Ivory
- CodeForces 219D 树形DP
- CodeForces 586A
- Codeforces 463D Gargari and Permutations(BFS)
- Codeforces 933 C. A Colourful Prospect (平面图,欧拉公式)
- Problem A CodeForces 148D 概率dp
- A. Watchmen(Codeforces 650A)