Codeforces Round #641 (Div. 1)
从此世界上又多了一个伤心的人。
A. Orac and LCM
题意:给n(1e5)个数,求他们两两lcm的gcd。
思路:两种思路。
思路一:设p为素数,如果最终答案包含p^k项,那么一定至少有n-1个数字包含p^k,因为此时任取两个数他们的lcm一定包含p^k。而如果只有n-2个数字包含,肯定存在两个数字他们的lcm不包含p^k,进而最终的答案不包括p^k。所以我们利用前缀gcd和后缀gcd维护出任意n-1个数字的gcd,它们一定包含于最终答案中,而最终答案不可能再包含其他因子,对他们取lcm即可。
思路二:对所有数字进行质因数分解,研究一个质数,两个数的lcm本质上就是两个数在对应这个质数的指数取较大值,gcd是取较小值,于是我们只需要找出当前质数倒数第二小的指数即可得到最终答案在这个质数处的指数(两两组合中较大值中的最小值)。(我的写法是nsqrt(n),事实上如果不进行质因数分解而直接枚举质数同时进行"找到两个指数为0的情况就结束"的优化可以使得复杂度变为nlog)
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 using namespace std; 17 const int N=1e5+10; 18 int n,a[N]; 19 vector<int>S[N*2]; 20 void get(int x){ 21 for(int i=2;i*i<=x;i++){ 22 if(x % i)continue; 23 int cnt=0; 24 while(x%i == 0)cnt++,x/=i; 25 S[i].push_back(cnt); 26 } 27 if(x > 1)S[x].push_back(1); 28 } 29 bool cmp(int x,int y){return x > y;} 30 LL ans; 31 int main(){ 32 // freopen("in.txt","r",stdin); 33 rd(n);for(int i=1;i<=n;i++)rd(a[i]),get(a[i]); 34 ans=1; 35 for(int i=2;i<=200000;i++){ 36 if(S[i].size() < n-1)continue; 37 sort(S[i].begin(),S[i].end(),cmp); 38 int u=S[i][n-2]; 39 for(int o=1;o<=u;o++)ans*=i; 40 } 41 printf("%lld\n",ans); 42 return 0; 43 } 44 /**/
B. Orac and Medians
题意:给一个长度为n(1e5)的序列,一次操作可以使得一段子序列变为该序列对应的中位数(n为奇数时为正中间,偶数时为下取整),问能否通过这种操作使得最终序列全变成k。
思路:可以发现一件事情,如果出现了长度至少为2的相同数字的序列,那么可以把整个序列都变成这个数,因为可以一个一个的向外扩展,依次使得相同数字序列长度变为3,4,5...
先说考场时的一个错误思路,似乎也是很多人的错误思路,有很多人都wa10了,这是其中一种情况:只考虑了对包含k的序列进行操作,进而便成了找到一个序列,<k的数的个数不到一半,<=k的数的个数超过一半,记录s1[i]表示i-2*"前缀中<k的数的个数",s2[i]对应<=,然后便成为了一个三维偏序的问题(没记错的话),利用线段树求最值的操作可以维护。然而这是一种错误的思路。因为操作时完全没必要包含k。下面给出一个反例。
9 3
5 5 1 1 1 1 1 3 1
同时贴上错误代码。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=1e5+10; 17 using namespace std; 18 int T; 19 int n,k,a[N]; 20 int s1[N],s2[N]; 21 int mx[N<<3]; 22 #define ls (o<<1) 23 #define rs (o<<1|1) 24 #define mid (l+r>>1) 25 void clear(int o,int l,int r){ 26 mx[o]=-0x7fffffff; 27 if(l == r)return ; 28 clear(ls,l,mid);clear(rs,mid+1,r); 29 } 30 void modify(int o,int l,int r,int x,int y){ 31 if(l == r){mx[o]=max(mx[o],y);return ;} 32 if(x <= mid)modify(ls,l,mid,x,y); 33 else modify(rs,mid+1,r,x,y); 34 mx[o]=max(mx[ls],mx[rs]); 35 } 36 int get(int o,int l,int r,int L,int R){ 37 if(l >= L && r <= R)return mx[o]; 38 int ans=-0x7fffffff; 39 if(mid >= L)ans=max(ans,get(ls,l,mid,L,R)); 40 if(mid < R)ans=max(ans,get(rs,mid+1,r,L,R)); 41 return ans; 42 } 43 int main(){ 44 // freopen("in.txt","r",stdin); 45 rd(T); 46 while(T--){ 47 rd(n);rd(k);for(int i=1;i<=n;i++)rd(a[i]);clear(1,1,2*n+1); 48 for(int i=1;i<=n;i++)s1[i]=s1[i-1]+(a[i]<k),s2[i]=s2[i-1]+(a[i]<=k); 49 for(int i=1;i<=n;i++)s1[i]=i-2*s1[i],s2[i]=i-2*s2[i]; 50 bool flg=0; 51 for(int i=2;i<=n;i++){ 52 modify(1,1,2*n+1,s1[i-2]+n+1,s2[i-2]); 53 if(s1[i]+n > 0 && get(1,1,2*n+1,1,s1[i]+n)>=s2[i]){flg=1;break;} 54 } 55 if(n == 1 && a[1] == k)flg=1; 56 if(flg)printf("yes\n"); 57 else printf("no\n"); 58 } 59 return 0; 60 } 61 /**/ 62
下面讲解正确思路:首先说明结论,只要包含k并且存在两个>=k且相距不超过2的数,则合法,否则不合法。长度为1时进行特判。
首先序列中不存在k的情况无论如何也不能最终变出来一个k。
如果存在两个>=k的数字且相距不超过2,那么我们可以以这两个数字为左右端点进行操作,可以得到一个长度>=2的序列,序列中的每个数>=k。以最开始提到的方式进行扩展,一直到与数值等于k的元素相邻,此时取包含k的长度为2的序列,便得到了一个长度等于2的k的序列,同样用最开始的方式进行扩展即可。
当不存在时,无论我们怎么取操作序列,操作过后都只能使得这一段全变成<k的元素,仍然不可能出现距离<=2的>=k的数字,再次回到刚刚的情况,无论重复多少次都只能变成<k的元素。
另外需要特判n=1的情况,此时只需要看他是不是k即可。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=1e5+10; 17 using namespace std; 18 int T,n,k,a[N]; 19 void work(){ 20 if(n == 1 && a[1] == k){printf("yes\n");return ;} 21 bool flg=0; 22 for(int i=1;i<=n;i++)if(a[i]==k)flg=1; 23 if(!flg){printf("no\n");return ;} 24 flg=0; 25 for(int i=1;i<n;i++)if(a[i] >= k && a[i+1] >= k)flg=1; 26 for(int i=1;i<n-1;i++)if(a[i] >= k && a[i+2] >= k)flg=1; 27 if(!flg)printf("no\n"); 28 else printf("yes\n"); 29 } 30 int main(){ 31 // freopen("in.txt","r",stdin); 32 rd(T); 33 while(T--){ 34 rd(n);rd(k); 35 for(int i=1;i<=n;i++)rd(a[i]); 36 work(); 37 } 38 return 0; 39 } 40 /**/
C. Orac and Game of Life
题意:n*m(1000)的黑白块,每次变化时,所有块同时进行判断,如果当前块周围有颜色相同的块,则改变颜色,否则不改变,t(1e5)次询问,每次询问一个块经过p(1e18)次变化后的颜色。
思路:如果相邻块颜色相同,变化完后还是颜色相同。我们把颜色相同的相邻块分成一组,每次变化就是这整组的块颜色变化,同时"对外扩充",即与组的边界相邻且不属于其他组的块合并为一组(可能因此导致组和组之间的合并,但不重要)。我们可以用bfs求出每个块进入“组”的时间。然后对于每次询问,如果时间小于对应块进入组的时间,就是其原本的颜色,否则可以根据进入组后变化次数的奇偶得到最终的颜色。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=1005; 17 using namespace std; 18 int n,m,t; 19 char s[N][N]; 20 LL dis[N][N]; 21 int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0}; 22 bool check(int x,int y){ 23 if(y != m && s[x][y] == s[x][y+1])return 1; 24 if(x != n && s[x][y] == s[x+1][y])return 1; 25 if(x != 1 && s[x][y] == s[x-1][y])return 1; 26 if(y != 1 && s[x][y] == s[x][y-1])return 1; 27 return 0; 28 } 29 struct ghb{ 30 int x,y; 31 }; 32 queue<ghb>S; 33 int main(){ 34 // freopen("in.txt","r",stdin); 35 rd(n);rd(m);rd(t); 36 for(int i=1;i<=n;i++)scanf("%s",s[i]+1); 37 memset(dis,0x7f,sizeof(dis)); 38 for(int i=1;i<=n;i++) 39 for(int j=1;j<=m;j++) 40 if(check(i,j))S.push((ghb){i,j}),dis[i][j]=0; 41 while(!S.empty()){ 42 ghb u=S.front();S.pop(); 43 for(int i=0;i<4;i++){ 44 int tx=u.x+dx[i],ty=u.y+dy[i]; 45 if(tx < 1 || tx > n)continue; 46 if(ty < 1 || ty > m)continue; 47 if(dis[tx][ty] > dis[u.x][u.y]+1){ 48 dis[tx][ty] = dis[u.x][u.y]+1; 49 S.push((ghb){tx,ty}); 50 } 51 } 52 } 53 for(int i=1;i<=t;i++){ 54 int x,y,col;LL p; 55 rd(x);rd(y);lrd(p); 56 col=s[x][y]-'0'; 57 if(dis[x][y] < p && ((p-dis[x][y])&1))col^=1; 58 printf("%d\n",col); 59 } 60 return 0; 61 } 62 /**/