Codeforces Round #636 (Div. 3)
A. Candies
题意
求出任意一个x,满足\(x+2x+4x+⋯+2^{k−1}x=n.\),k为任意大于1的数。\((3≤n≤10^9)\)
思路
将2的指数的前缀和大表存在Map,然后枚举k,只需要枚举1到sqrt(n)即可。
代码
#include<bits/stdc++.h>
using namespace std;
map<long long,int> mmp;
int main(){
int T;
long long p=1,sum=0;
for(int i=1;i<=32;++i){
sum+=p;
mmp[sum]=i;
p*=2;
}
mmp[1]=0;
cin>>T;
while(T--){
int n;
cin>>n;
for(int i=1;i<=sqrt(n);++i){
if(n%i==0&&mmp[n/i]){
cout<<i<<endl;
break;
}
else if(n%i==0&&mmp[i]){
cout<<n/i<<endl;
break;
}
}
}
return 0;
}
B. Balanced Array
题意
构造一个长度为n的序列满足:前n/2个数字是偶数,后n/2个数字是奇数,且前n/2个数之和等于后n/2个数之和。\((2≤n≤2⋅105)\)
思路
假设让前n/2个数等于2到n之间的偶数,后n/2-1个数等于1到n之间的奇数,将前后的差补在最后一个数上,使得最后一个数是奇数,否则输出“NO”.
代码
#include<bits/stdc++.h>
using namespace std;
int a[200010];
int main(){
int T;
cin>>T;
int t=2;
for(int i=1;i<=100000;++i,t+=2){
a[i]=t;
}
t=1;
for(int i=100001;i<=200000;++i,t+=2){
a[i]=t;
}
while(T--){
int n;
cin>>n;
if((n/2)%2){
cout<<"NO\n";
continue;
}
cout<<"YES\n";
for(int i=1;i<=n/2;++i){
cout<<a[i]<<" ";
}
for(int i=100001;i<=100000+n/2;++i){
if(100000+n/2==i){
cout<<a[i]+n/2;
}
else cout<<a[i]<<" ";
}
cout<<endl;
}
return 0;
}
C. Alternating Subsequence
题意
给出一个只有正负数的序列,求出最长的摆动子序列(正负交替),在子序列最长的前提下,子序列之和尽可能地大。\((1≤n≤2⋅105)\)
思路
模拟:对正负性相同的连续一段选出值最大的一个加入到序列中去。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL f[200010][2],a[200010];
int main(){
int T;
cin>>T;
while(T--){
int n;
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
}
LL ans=0;
for(int i=1;i<=n;){
LL tmp=-1e18;
int j=i;
for(;j<=n;++j){
if(a[i]*a[j]>=0){
tmp=max(a[j],tmp);
}
else break;
}
i=j;
ans+=tmp;
}
cout<<ans<<endl;
}
return 0;
}
D. Constant Palindrome Sum
题意
将一个序列的某一个数字替换成1到k的任意一个数,使得最终的序列满足对于所有的\(i∈(1,n/2)\)有:\(ai+an−i+1=x\)。\((2≤n≤2⋅10^5,1≤k≤2⋅10^5,1≤ai≤k)\)
思路
x可以等于2到2k之间的任意一个数,那么我们枚举每个二元组,求出不改变时它可以取到的和,改变一个可以取到的和,改变两个可以取到的和各有哪些。
那么对于可以取到的值去差分,最后求出最小的一个值,就是操作次数最少可以得到的x。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t[200010],n,m;
int f[1000010];
signed main(){
int T;
cin>>T;
while(T--){
cin>>n>>m;
for(int i=0;i<=2*m;++i){
f[i]=0;
}
for(int i=1;i<=n;++i){
cin>>t[i];
}
for(int i=1;i<=n/2;++i){
int a=t[i],b=t[n-i+1];
int l1=b+1,r1=b+m;
int l2=a+1,r2=a+m;
f[min(l1,l2)]++;
f[a+b]--;
f[a+b+1]++;
f[max(r1,r2)+1]--;
f[2]+=2;
f[min(l1,l2)]-=2;
f[max(r1,r2)+1]+=2;
f[2*m+1]-=2;
}
int ans=1000000;
for(int i=2;i<=m*2;++i){
f[i]+=f[i-1];
ans=min(ans,f[i]);
}
cout<<ans<<endl;
}
return 0;
}
E. Weights Distributing
题意
在一个暂未赋权有m条边的无向图的和m个权值,求出存在有一种赋权的最优方案从a到b到c的最小花费。
思路
一定只有两种路线即:
- a -> v -> b -> v -> c,即从a到v到b再折返回v到c。
- a -> b -> c,即从a到b到c没有走重边,那么也可以变形为:a -> b -> b -> b -> c。
预处理a,b,c到每个点经过的最少边数,再将p排序求出前缀和。由于b到v的过程走了两次,所以a到b的边权尽可能地小,再取a到v和c到v的边权。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=200010;
int h[N],idx;
typedef long long LL;
LL p[N*2];
int d1[N],d2[N],d3[N],q[N];
struct eg{
int v,nex;
}e[N*2];
void add(int u,int v){
e[idx]={v,h[u]};
h[u]=idx++;
}
void bfs(int d[],int S){
int hh=0,tt=0;
q[tt++]=S;
d[S]=0;
while(hh!=tt){
int u=q[hh++];
if(hh==N) hh=0;
for(int i=h[u];~i;i=e[i].nex){
int v=e[i].v;
if(d[v]>d[u]+1){
d[v]=d[u]+1;
q[tt++]=v;
if(tt==N) tt=0;
}
}
}
}
int main(){
int T;
cin>>T;
while(T--){
memset(h,-1,sizeof h);idx=0;
memset(d1,0x3f,sizeof d1);
memset(d2,0x3f,sizeof d2);
memset(d3,0x3f,sizeof d3);
int n,m,a,b,c;
cin>>n>>m>>a>>b>>c;
for(int i=1;i<=m;++i) cin>>p[i];
sort(p+1,p+1+m);
for(int i=1;i<=m;++i) p[i]+=p[i-1];
for(int i=1;i<=m;++i){
int u,v;
cin>>u>>v;
add(u,v);
add(v,u);
}
bfs(d1,a);
bfs(d2,b);
bfs(d3,c);
LL ans=1e18;
for(int i=1;i<=n;++i){
int t1=d2[i],t2=d1[i]+d3[i];
if(t1+t2<=m){
ans=min(ans,p[t1]*2+p[t1+t2]-p[t1]);
}
}
cout<<ans<<endl;
}
return 0;
}