Codeforces Round #628 (Div. 2)
A. EhAb AnD gCd
题意:找两个数使得他们的gcd和lcm之和为x。
思路:取1和x-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 using namespace std; 17 int T; 18 int x; 19 int main(){ 20 // freopen("in.txt","r",stdin); 21 rd(T); 22 while(T--){ 23 rd(x); 24 printf("%d %d\n",1,x-1); 25 } 26 return 0; 27 } 28 /**/
B. CopyCopyCopyCopyCopy
题意:给n(1e5)个数字的序列,将它们复制为n份首尾相接,问最长上升子序列是多少(严格上升)。
思路:原序列有多少个不同的数字,最终答案就是多少。我们可以在每一份取一个值来使得其单调上升。并且由于是严格上升,最终答案也不可能超过不同数字的个数。
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,a[N]; 20 set<int>S; 21 int main(){ 22 // freopen("in.txt","r",stdin); 23 rd(T); 24 while(T--){ 25 S.clear();int ans=0; 26 rd(n);for(int i=1;i<=n;i++){rd(a[i]);if(S.find(a[i])==S.end())ans++;S.insert(a[i]);} 27 printf("%d\n",ans); 28 } 29 return 0; 30 } 31 /**/
C. Ehab and Path-etic MEXs
题意:n(1e5)个节点的树,将0~n-2赋值到每条边上,使得对于所有的u,v,MEX(u,v)的最大值最小。MEX(u,v)表示uv的简单路径上未出现的最小非负整数的值。
思路:
方案一:对于所有叶子节点所连边依次赋值0,1,2...其余的边任意赋值。我们可以发现,MEX(u,v)最大的uv一定都对应叶子节点,否则将其延伸至叶子节点MEX(u,v)一定不会变小。同时可以发现一条路径上最多只有两个叶子节点,也就是按按上述方式赋值,可以保证最终答案就是2。而答案永远不可能小于2,因为0和1对应的两条边总可以通过一种方式将它们连接起来。当然,2个节点的情况要注意特判。
方案二:寻找一个度数大于等于三的节点(如果不存在这样的节点,那么就是一条链,答案一定为n-1),将它的三条边赋值0,1,2,其它的值任意取,最终答案也能保证是2。因为0,1,2永远不可能同时被取到。
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 n; 19 int fi[N],nxt[N<<1],to[N<<1],cst[N],tot; 20 void link(int x,int y){nxt[++tot]=fi[x];fi[x]=tot;to[tot]=y;} 21 int in[N]; 22 int main(){ 23 // freopen("in.txt","r",stdin); 24 rd(n); 25 for(int i=1;i<n;i++){ 26 int x,y;rd(x);rd(y); 27 link(x,y);link(y,x); 28 in[x]++;in[y]++; 29 } 30 int now=0; 31 for(int i=1;i<=n;i++){ 32 if(in[i] != 1)continue; 33 if(!cst[(fi[i]+1)/2])cst[(fi[i]+1)/2]=++now; 34 } 35 for(int i=1;i<n;i++)if(!cst[i])cst[i]=++now; 36 for(int i=1;i<n;i++)printf("%d\n",cst[i]-1); 37 return 0; 38 } 39 /**/
D. Ehab the Xorcist
题意:找若干个数使得他们的和为v(0~1e18),异或和为u(0~1e18)。尽可能使得找到的数的个数最少,输出方案。不合法输出-1。
思路:存在公式a+b=(a^b)+2*(a&b),所以v必定是大于等于u的,而且奇偶性必然相同。所以只需要考虑v>=u且u%2==v%2的情况,我们可以发现一定可以构造出一组长度为3的解,即u,(v-u)/2,(v-u)/2,那么最终的答案就有了一个上界3。0个是u=v=0的特殊情况对应答案,1是u=v的特殊答案。考虑什么时候可以出现2的情况,如果u& ((v-u)/2)=0的话,我们就可以将二者合并为一个数字,那么就会出现2的情况。再考虑是否存在其他2的情况。不妨令两个数为x,y,按位考虑,如果u的当前位为1,那么xy中当前位分别为1和0,谁是1谁是0并不会对二者之和造成影响,不会影响构造,不妨令x为1,y为0,在考虑完所有1对应位的构造之后,x=u,y=0。如果u的当前位为0,那么xy的当前位的值其实是固定的,同为1或者同为0,其实也就是由(v-u)/2的对应位所决定的,而这必须保证和1对应位不发生冲突,其实条件也就是u&((v-u)/2)=0。最终两个数可以有很多种组合,但是判断的条件都是一样的。
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=2e7+10; 17 using namespace std; 18 LL u,v; 19 int main(){ 20 // freopen("in.txt","r",stdin); 21 lrd(u);lrd(v); 22 if(v < u || v%2 != u%2)printf("-1\n"); 23 else if(u==0 && v==0)printf("0\n"); 24 else if(u == v)printf("1\n%lld\n",u); 25 else { 26 LL ans[3];ans[0]=u;ans[1]=ans[2]=(v-u)/2;int cnt=3; 27 if((ans[0] & ans[1]) == 0)ans[0]|=ans[1],cnt--; 28 printf("%d\n",cnt);for(int i=0;i<cnt;i++)printf("%lld ",ans[i]);puts(""); 29 } 30 return 0; 31 } 32 /*a+b=(a^b)+2*(a&b)*/
反思:a+b=(a^b)+2*(a&b)。学会u,(v-u)/2,(v-u)/2这种构造思路,取两个相同的数字就改变和而不改变异或和。
E. Ehab's REAL Number Theory Problem
题意:给n(1e5)个数ai(1~1e6),每个数的约数不超过7个,要求选出最少的数使得他们的乘积是一个完全平方数,输出选择的个数即可,如果不存在合法方案输出-1。
思路:我以为是dp之类的,然而不是。这是一道神奇的图论题。先看预处理过程。对于ai,我们将其中所包含的平方因子除去,对最终答案的选择不会有影响,并且约数个数不会增加,那么此时将其分解质因数后,每个质因数的个数至多为1个。如果是由3个质因子组成的,那么它有8个约数,所以ai至多由两个质因子组成,那么可以分为三种情况1,p,p*q,(p,q为素数)。如果存在1的情况,我们就可以直接找到完全平方数了,题目结束。所以只剩下p和p*q。我们不妨将p看成p*1。考虑ai=p*q,我们将ai和p,q两个因子分别连边得到一张图。可以发现乘积为完全平方数对应图上的一个环,要使选择的数个数最少,也就是要求选择的环最小。问题便转换为了寻找一张图上的最小环。无权值的情况下求最小环是可以在n^2的时间内做到的,即对于每个点bfs,一旦bfs到已访问过的点,这两个点到他们的lca便形成了一个环,而事实上我们直接假设lca是根来更新答案即可,因为如果不是的话并不会使得答案变得更小,并且后面会再次计算到这个环,因为对所有点都进行了bfs,所以总会对最小环上的点bfs,这时得到该环的两点的lca就是根了。需要注意这里不可以用dfs,因为dfs会由于加边顺序的不同找到的环也不同,找到的环可能不是最小的,具体情况可以看我代码中给出的注释。而如何将n^2优化,我们可以发现ai的上限是1e6,也就是一定存在一个<=1000的因子,所以所有的环上都包括1~1000的某个值对应的点,那么我们只需要对这些点进行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=1e5+10; 17 using namespace std; 18 int n; 19 int b[N][2]; 20 int fi[N*11],to[N<<2],nxt[N<<2],tot; 21 void link(int x,int y){nxt[++tot]=fi[x];fi[x]=tot;to[tot]=y;} 22 int ans,dep[N*11]; 23 //dfs不能用,可能导致得到的环长度不是最短,比如1->2,2->3,3->4,4->5,3->5,5->1 24 void clear(int x){ 25 dep[x]=-1; 26 for(int i=fi[x];i;i=nxt[i]) 27 if(dep[to[i]] != -1)clear(to[i]); 28 } 29 queue<pair<int,int> >S; 30 void bfs(int x){ 31 clear(x);S.push(make_pair(x,0));dep[x]=0; 32 while(!S.empty()){ 33 pair<int,int> u=S.front();S.pop(); 34 for(int i=fi[u.first];i;i=nxt[i]) 35 if(dep[to[i]] == -1){ 36 dep[to[i]]=dep[u.first]+(to[i]<=n); 37 S.push(make_pair(to[i],u.first)); 38 } 39 else if(to[i] != u.second)ans=min(ans,dep[u.first]+dep[to[i]]); 40 } 41 } 42 int main(){ 43 // freopen("in.txt","r",stdin); 44 rd(n);bool flg=0; 45 for(int i=1;i<=n;i++){ 46 int x;rd(x); 47 int tmp1=sqrt(x),tmp2=0; 48 for(int j=2;j<=tmp1;j++){ 49 if(x % j)continue;int cnt=0; 50 while(x % j ==0)x/=j,cnt++; 51 if(cnt & 1)b[i][tmp2++]=j; 52 } 53 if(x != 1)b[i][tmp2++]=x; 54 if(tmp2 == 0){flg=1;break;} 55 if(tmp2 == 1)b[i][tmp2]=1; 56 link(i,n+b[i][0]);link(n+b[i][0],i); 57 link(i,n+b[i][1]);link(n+b[i][1],i); 58 } 59 if(flg)printf("1\n"); 60 else { 61 ans=0x7fffffff; 62 for(int i=n+1;i<=n+1000;i++)if(fi[i])bfs(i); 63 if(ans == 0x7fffffff)ans=-1; 64 printf("%d\n",ans); 65 } 66 return 0; 67 }
F. Ehab's Last Theorem
题意:n(1e5)个点m(2e5)条边的图,设sq=根号n上取整,找到一个大小为sq的独立子集或者找到一个长度大于等于sq的简单环,输出方案,题目保证至少有一个可以求出来。
思路:考虑dfs树的做法来求最大环。任找一个点dfs,搜到已访问的点就出现了环,判断环的大小是否大于等于sq,虽然不能找到所有的环,但能找到所有比较大的环。有关dfs树的知识可以看官方题解给的blog,写的相当好。如果最后都没能找到大于等于sq的环,那么必然不存在两个相连的点深度差大于等于sq-1的(dfs树上的非树边一定是连接了祖先和儿子),那么对于所有的点按它们的深度%(sq-1)分类,必然存在至少一种,它包含了至少sq个互不相连的点(抽屉原理),从中取sq个即可。
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 n,m; 19 int fi[N],nxt[N<<2],to[N<<2],tot; 20 void link(int x,int y){nxt[++tot]=fi[x];fi[x]=tot;to[tot]=y;} 21 int dep[N],sq,fa[N]; 22 vector<int>S[N]; 23 void dfs(int x){ 24 dep[x]=dep[fa[x]]+1;S[dep[x]%(sq-1)].push_back(x); 25 for(int i=fi[x];i;i=nxt[i]){ 26 if(!dep[to[i]])fa[to[i]]=x,dfs(to[i]); 27 else if(dep[x]-dep[to[i]]+1 >= sq){ 28 printf("%d\n%d\n",2,dep[x]-dep[to[i]]+1); 29 while(x != to[i])printf("%d ",x),x=fa[x]; 30 printf("%d\n",x); 31 exit(0); 32 } 33 } 34 } 35 int main(){ 36 // freopen("in.txt","r",stdin); 37 rd(n);rd(m); 38 sq=sqrt(n);if(sq*sq < n)sq++; 39 for(int i=1;i<=m;i++){ 40 int x,y;rd(x);rd(y); 41 link(x,y);link(y,x); 42 } 43 dfs(1);printf("%d\n",1); 44 for(int i=0;i<sq-1;i++) 45 if(S[i].size() >= sq){ 46 for(int j=0;j<sq;j++) 47 printf("%d ",S[i][j]); 48 break; 49 } 50 puts(""); 51 return 0; 52 } 53 /**/
反思:学会dfs树找环。特殊情况求独立子集不是np问题。