Codeforces Round #565 (Div. 3)
A. Divide it!
•题意
给定一个数n, 每次可以进行下列一种操作
1.如果n可以被2整除,用n/2代替n
2.如果n可以被3整除,用2n/3代替n
3.如果n可以被5整除,用4n/5代替n
如果可以经过上述操作使得 n 变为 1,输出最小操作次数,反之,输出-1;
•思路
n/2 < 2n/3 < 4n/5 要想操作次数最少,优先操作 1 > 2 > 3;
•代码
View Code#include<bits/stdc++.h> using namespace std; #define ll long long int a[105]; int main() { int t; cin>>t; while(t--) { ll n; cin>>n; int flag=0; int Count=0; while(n>1) { if(n%2==0)//1 { n/=2; Count++; } else if(n%3==0)//2 { n=n/3*2; Count++; } else if(n%5==0)//3 { n=n/5*4; Count++; } else//不能被整除,即经过操作不能变为1 { flag=1; break; } } if(flag) cout<<-1<<endl; else cout<<Count<<endl; } }
B. Merge it!
•题意
给你一个包含 n 个数的序列 a;
定义序列 a 上的一个操作:合并任意两个元素;
你可以对序列 a 执行上述操作任意次,求操作后的序列最多有多少元素可以被 3 整除;
•思路
对于任意一个数x
1.如果x是3的倍数,x%3==0
如果x不是3的倍数
2.如果x+1是3的倍数,x%3==1,3*x%x==0
3.如果x+2是3的倍数,x%3==2,3*x%x==0
如果想尽可能多的是三的倍数:
首先加x%3==0的x的个数,这样只使用一个数
其次2和3相加正好是3的倍数,这样只使用两个数
最后还剩下2或者3,则自身结合,这样是使用三个数
•代码
View Code#include<bits/stdc++.h> using namespace std; int a[105]; int main() { int t; cin>>t; while(t--) { int n; cin>>n; int zero=0,one=0,two=0;//分别记录x%3为0,1,2的个数 for(int i=0;i<n;i++) { cin>>a[i]; a[i]%=3; if(a[i]==0) zero++; else if(a[i]==1) one++; else two++; } // cout<<zero<<' '<<one<<' '<<two<<endl; if(one>=two) cout<<zero+two+(one-two)/3<<endl; else cout<<zero+one+(two-one)/3<<endl; } }
C. Lose it!
•题意
给你一个包含 n 个整数的序列 a和good序列{4,8,15,16,23,42};
在删去 x 个数后,使得序列 a 可以划分成 (n-x) / 6 个 "good" 序列;
求 x 的最小值;
•题解
求出序列 a 最多有多少个 "good" 序列(假设有 ans 个),需要删去的个数就是 n-6×ans;
•代码
View Code#include<bits/stdc++.h> using namespace std; const int maxn=500005; vector<int> p[43]; int s[maxn]; int t[]={0,4,8,15,16,23,42}; int ans=0;//一共有ans个good串 int a[50];//记录使用后的每个字母的最后一个位置,即这个字母到达的最远的位置 //后面再找这个字母时,从这个位置的下一个开始找,可以减少查找量 int main() { int n; cin>>n; for(int i=1;i<=n;i++) { cin>>s[i]; p[s[i]].push_back(i);//预处理s串每个good数的位置 } int cur=0; int flag; for(int i=1;i<=6;i++) { flag=0; for(int j=a[t[i]];j<p[t[i]].size();j++) { flag=0; if(p[t[i]][j]>cur)//在s串中找x出现的位置 >cur 的第一个位置,有点贪心的感jio { cur=p[t[i]][j];//更新cur a[t[i]]=j+1;//记录之前x到达的最远位置j,后面从j+1开始找 flag=1; break; } } if(!flag) break; if(cur>n) break; if(i==6)//找完一次,再从第一个good数找下一次 { ans++; cur=0; i=0; } } cout<<n-ans*6<<endl; }
D. Recover it!
•题意
有一个数组a和一个数组b,其中b是这样组成的
①b中包含a中所有的数
②对于a中的数,如果ai是素数,那么在b中添加第ai个素数
如果ai是合数,那么在b中添加ai的最大约数(除ai外的)
如果a中有n个数,那么b中包含如上的2*n个数
现给出b中的所有数,求a中的所有数
•思路
对于b中的数 ,我们可以考虑从大到小排序,
①如果最大的这个数是合数的话,那么他一定是a中的数,因为在b中添加的是比ai
小的最大约数
②如果是素数的话,那么他一定是被添加到b中的数,也就是一定不是a中的数,而他所对应的这个素数的位置才是a中的数
•代码
View Code#include<bits/stdc++.h> using namespace std; const int maxn=2e5+5; const int mm=2750131; int Mark[mm+10]; int prime[maxn]; int index; void Prime() { memset(Mark,0,sizeof(Mark)); for(int i=2;index<=199999;i++) { if(Mark[i]==0) prime[index++]=i; for(int j=0;j<index&&prime[j]*i<=mm;j++) { Mark[i*prime[j]]=1; if(i%prime[j]==0) break; } } } map<int,int> mp;//用map存便于找是减少数量 int a[maxn],b[2*maxn]; int main() { Prime(); int n; cin>>n; for(int i=0;i<2*n;i++) { cin>>b[i]; mp[b[i]]++; } int cur=0; sort(b,b+2*n); for(int i=2*n-1;i>=0;i--) { if(!mp[b[i]]) continue; if(cur>n) break; if(Mark[b[i]])//合数 { a[cur++]=b[i];//加到a数组中 for(int j=2;;j++)//找最大的约数 { if(b[i]%j==0) { mp[b[i]/j]--;//删去这个约数 break; } } } else//素数 { int p=1+lower_bound(prime,prime+index,b[i])-prime;//这个素数的位置 a[cur++]=p;//加到a数组中 mp[p]--;//删去这个代表位置的素数 } mp[b[i]]--;//删去已经找完的这个数 } for(int i=0;i<n;i++) cout<<a[i]<<' '; }用map存是从李先生那偷过来的(QWQ)
E. Cover it!
•题意
从含有 n 个点,m 条边的无向图中取出 x 个点,这 x 个点需满足:
① x ≤ n/2;
②剩余的点至少与这 x 个点中的一个点有连边;
输出满足条件的这 x 个点;
•题解
李先生说这是二部图裸题,据他说,突然想到我们老班离散课上讲的知识,可能是我没听讲???(还不是因为太菜惹
找出集合 U 和集合 V,输出 |U| 和 |V| 中元素个数最少的集合;
以任意一个点开始,加入把他涂成红色,那和他相连的点涂成绿色,和这个点相邻的量再涂成红色....以此
就分成了两个集合,输出点数较少的那个集合
•代码
bfs涂色
View Code#include<bits/stdc++.h> using namespace std; const int maxn=200005; int x,y; int cntx,cnty; struct Edge { int u; int v; int nex; }edge[2*maxn]; struct Data//涂色 { int v; int col; }; int head[maxn],vis[maxn],cnt; int a[maxn],b[maxn];//0:红色,b:绿色 a[]为0集合,b[]为1集合 void add(int u,int v) { edge[++cnt].u=u; edge[cnt].v=v; edge[cnt].nex=head[u]; head[u]=cnt; } queue<Data>q; void paint() { while(!q.empty())//bfs涂色 { Data tmp=q.front(); int u=tmp.v;//这个点涂色 q.pop(); for(int i=head[u];i;i=edge[i].nex) { int v=edge[i].v; if(!vis[v]) { vis[v]=1; q.push(Data{v,!tmp.col});//和他相连的点涂相反的颜色 if(tmp.col)//分在两个集合里 a[++cntx]=v; else b[++cnty]=v; } } } } int main() { int t; cin>>t; while(t--) { int n,m; cin>>n>>m; for(int i=1;i<=n;i++) vis[i]=0,head[i]=0,a[i]=0,b[i]=0; cnt=0; cntx=0,cnty=0; for(int i=0;i<m;i++) { cin>>x>>y; add(x,y); add(y,x); } while(!q.empty()) q.pop(); q.push(Data{x,0});//以一个点开始 a[++cntx]=x; vis[x]=1; paint(); if(cntx<=n/2) { cout<<cntx<<endl; for(int i=1;i<=cntx;i++) cout<<a[i]<<' '; } else { cout<<cnty<<endl; for(int i=1;i<=cnty;i++) cout<<b[i]<<' '; } cout<<endl; } }dfs涂色
View Code#include<bits/stdc++.h> using namespace std; const int maxn=1e5+5; struct Edge { int v; int nex; }edge[4*maxn]; int head[maxn*2]; int cnt; void add(int u,int v) { edge[++cnt]=Edge{v,head[u]}; head[u]=cnt; } int col[maxn*2];//染色数组分为0,1两组代表红、绿 void paint(int u,int flag)//dfs涂色 { col[u]=flag;//u点染成flag色 for(int i=head[u];i;i=edge[i].nex)//下一个未染色的点染成和他相反的颜色 { int v=edge[i].v; if(col[v]==-1) paint(v,!flag); } } int main() { int t; cin>>t; while(t--) { cnt=0; int n,m; cin>>n>>m; for(int i=1;i<=n;i++) head[i]=0,col[i]=-1; int x,y; for(int i=0;i<m;i++) { cin>>x>>y; add(x,y); add(y,x); } paint(x,0); int ans=0; for(int i=1;i<=n;i++) if(col[i]) ans++; if(ans>n/2) { cout<<n-ans<<endl; for(int i=1;i<=n;i++) if(!col[i]) cout<<i<<' '; } else { cout<<ans<<endl; for(int i=1;i<=n;i++) if(col[i]) cout<<i<<' '; } cout<<endl; } }ps.偷偷学了一下李先生的链式前向星加边,比我以前的方法省了好多代码^(*-.-*)^