Codeforces Round #525 (Div. 2)
题目直通车:http://codeforces.com/contest/1088
A - Ehab and another construction problem
开心一下
#include<bits/stdc++.h> using namespace std; #define freread freopen("input.txt","r",stdin); #define frewrite freopen("output.txt","w",stdout); typedef long long ll; const int maxn=5e5+7; int n,m,k,ar[maxn]; int main() { scanf("%d",&n); if(n==1)printf("-1\n"); else printf("%d %d\n",n,n); return 0; }
B - Ehab and subtraction
排序之后以此输入当前值和前缀和的差值即可,注意差值为0时要跳过
#include<bits/stdc++.h> using namespace std; #define freread freopen("input.txt","r",stdin); #define frewrite freopen("output.txt","w",stdout); typedef long long ll; const int maxn=5e5+7; int n,m,k,ar[maxn]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",ar+i); int ins=0; sort(ar+1,ar+n+1); ll pre=0,ans=0; for(int i=1;i<=m;i++){ ins++; while(ins<=n && ar[ins]==pre)ins++; if(ins<=n)ans=ar[ins]-pre,pre=ar[ins]; else ans=0; printf("%lld\n",ans); } return 0; }
C - Ehab and a 2-operation task
题目设定n+1其实说的很明显了
设置n次+操作,1次%操作,每次+操作使得序列的每一个值对一个大数(1000000)的模为递减序列即可,例如我的最后一个模值为999999,往前以此递减
#include<bits/stdc++.h> using namespace std; #define freread freopen("input.txt","r",stdin); #define frewrite freopen("output.txt","w",stdout); typedef long long ll; const int maxn=5e5+7; int n,m,k,ar[maxn]; ll mx=1000000; int main() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",ar+i); printf("%d\n",n+1); ll ins=999999; ll pre=0; for(int i=n;i>=1;i--){ ll ans=(ins-(pre+ar[i])%mx+mx)%mx; pre += ans; printf("1 %d %lld\n",i,ans); ins--; } printf("2 %d %lld\n",n,mx); return 0; }
D - Ehab and another another xor problem
考虑二进制查询,根据最多需要62次的提示可知,平均每两次查询需要得到a,b当前的二进制位值,二进制位共有(1,0),(1,1),(0,0),(0,1)四种情况,可以用一次查询来得出当前a,b的大小情况(查询? 0 0),假设a<b,则共有(0,0),(0,1),(1,1)三种情况,,此时发现,如果a,b在该二进制位上相等的话,,下次就不需要再进行大小判断的查询(节省一次查询),,则就可以想到,在本次查询中,希望能用一次来确定a,b是不是(0,1)的情况,而可以用两次来判断是(0,0)或者(1,1)(下一个二进制位不需要大小判断而节省下来的一次查询机会),则可以用print(! a|(1<<ins),b|(1<<ins))(ins为当前查询的二进制位),此时如果得到的是大于,则可以判定a,b的在二进制的ins位上为(0,1),否则,再用一次print(! a|(1<<ins),b)来确定本二进制位是(0,0)还是(1,1);反之,相同;
#include<bits/stdc++.h> using namespace std; #define freread freopen("input.txt","r",stdin); #define frewrite freopen("output.txt","w",stdout); typedef long long ll; const int maxn=5e5+7; int n,m,k,ar[maxn]; bool fond=0; int a,b; int main() { a=0,b=0; fond=0; int pre=-2,ins=29,mid; while(ins>=0){ if(pre==-2){ printf("? %d %d\n",a,b); fflush(stdout); mid=pre; scanf("%d",&pre); } else if(pre==-1){ printf("? %d %d\n",a|(1<<ins),b|(1<<ins)); fflush(stdout); scanf("%d",&pre); if(pre==1){ b |= (1<<ins); ins--; pre=-2; } else{ mid=pre; pre=0; } } else if(pre==1){ printf("? %d %d\n",a|(1<<ins),b|(1<<ins)); fflush(stdout); scanf("%d",&pre); if(pre==-1){ a |= (1<<ins); ins--; pre=-2; } else{ mid=pre; pre=0; } } else if(pre==0){ pre=mid; printf("? %d %d\n",a|(1<<ins),b); fflush(stdout); scanf("%d",&mid); if(mid==1){ //cout<<"input 0\n"; ins--; } else{ //cout<<"input 1\n"; a |= (1<<ins); b |= (1<<ins); ins--; } } //cout<<"pre = "<<pre<<endl; } printf("! %d %d\n",a,b); return 0; }
E - Ehab and a component choosing problem
题意:取k个联通块,使得所取的点的权值和/k最大,相等时,k最大
以权值最大的点为根,dfs遍历图,如果当前联通块权值和为+,即对父节点的贡献为正,否则无贡献,求出权值最大的联通块即可(k==1),重点在于如何使得联通块无重合的情况下k尽量大,此时需要新加一个变量来说明当前联通块是否对答案有贡献,如果有且根父节点所在联通块的最大值一样的话,不能同时对k做贡献,相应控制此变量即可(代码中的my);
需要注意的是,当父节点的一个子节点更新了最大值时,之前遍历的其他子节点对答案的贡献要清零,即重置my变量
#include<bits/stdc++.h> using namespace std; #define freread freopen("input.txt","r",stdin); #define frewrite freopen("output.txt","w",stdout); typedef long long ll; const int maxn=5e5+7; int n,m,k; ll ar[maxn],mn=-1e16-1; vector<int> ve[maxn]; ll a1,a2; ll dfs(int u,int fa,bool& my){ ll son=ar[u]; for(int i=0;i<ve[u].size();i++){ int v=ve[u][i]; if(v==fa)continue; bool ss=0; ll pre=a1; ll mid=dfs(v,u,ss); if(mid!=mn){ son+=mid; my |= ss; } if(a1>pre && ss==0)my=0;//如果最大值改变且当前子节点不对答案有贡献,则my清零 } if(son>a1){ a1=son; a2=1; my=1; } else if(son==a1 && my==0){ a2++; my=1; } if(son>0)return son; my=0; return mn; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%lld",ar+i); for(int i=1;i<n;i++){ int a,b;scanf("%d%d",&a,&b); ve[a].push_back(b); ve[b].push_back(a); } ll mid=mn,ins=0; for(int i=1;i<=n;i++){ if(mid<ar[i]){ mid=ar[i]; ins=i; } } bool my=0; a1=mn,a2=1; dfs(ins,0,my); printf("%lld %lld\n",a1*a2,a2); return 0; } /* input: 4 3 -3 3 3 1 2 2 3 3 4 output: 6 1 *
F - Ehab and a weird weight formula
由given tree的性质可以知道,以最小权值的节点作为根节点dfs的话,每个子节点权值都比父节点要大
对每一个子节点,可以将他的父节点重置为他的某个祖先节点(把该节点作为相应祖先节点的子节点),在离祖先节点距离为同一个ceil(dist(u,v))时,肯定是祖先节点越远越好(贪心),即需要遍历离此节点距离为1,2,4,8,16。。。的祖先节点,求对答案贡献值最小的祖先节点即可。复杂度nlog
#include<bits/stdc++.h> using namespace std; #define freread freopen("input.txt","r",stdin); #define frewrite freopen("output.txt","w",stdout); typedef long long ll; const int maxn=5e5+7; int n,m,k,ar[maxn]; vector<int> ve[maxn]; int mn[maxn],len; ll ans; void dfs(int u,int fa){ for(int i=0;i<ve[u].size();i++){ int v=ve[u][i]; if(v==fa)continue; mn[++len]=ar[v]; ll mid=1e12+1; for(int j=0;j<20;j++){ int x=len-(1<<j); x=max(x,1); mid=min(mid,1LL*mn[x]*(j+1)); if(x==1)break; } ans += ar[v]; ans += mid; dfs(v,u); len--; } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",ar+i); for(int i=1;i<n;i++){ int a,b;scanf("%d%d",&a,&b); ve[a].push_back(b); ve[b].push_back(a); } int ins=0,mx=1e9+1; for(int i=1;i<=n;i++){ if(mx>ar[i]){ mx=ar[i]; ins=i; } } ans=0; len=0; mn[++len]=ar[ins]; dfs(ins,0); printf("%lld\n",ans); return 0; }