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
",1,x-1);
25  }
26  return 0;
27 }
28 /**/
View Code

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
",ans);
28  }
29  return 0;
30 }
31 /**/
View Code

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
",cst[i]-1);
37  return 0;
38 }
39 /**/
View Code

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
");
23  else if(u==0 && v==0)printf("0
");
24  else if(u == v)printf("1
%lld
",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
",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)*/
View Code

反思: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
");
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
",ans);
65  }
66  return 0;
67 }
View Code

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
%d
",2,dep[x]-dep[to[i]]+1);
29    while(x != to[i])printf("%d ",x),x=fa[x];
30    printf("%d
",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
",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 /**/
View Code

反思:学会dfs树找环。特殊情况求独立子集不是np问题。

原文地址:https://www.cnblogs.com/hyghb/p/12507166.html