题目链接:
A.Candies
B.Balanced Array
C.Alternating Subsequence
D.Constant Palindrome Sum
E.Weights Distributing
F.Restore the Permutation by Sorted Segments
- A.Candies
思路:简单枚举即可
AC代码:
void solve() {
cin>>n;
for(ll i=3,k=4;;i+=k,k*=2) {
if(n%i==0) {
cout<<n/i<<'\n';
return ;
}
}
}
- B.Balanced Array
思路:简单模拟
AC代码:
void solve(ll sum=0) {
cin>>n;
if(((n+1)/2)&1) {
cout<<"NO\n";
return ;
}
cout<<"YES\n";
rep(i,n/2) {
cout<<i*2<<' ';
sum+=i*2;
}
rep(i,(n+1)/2-1) {
cout<<i*2-1<<' ';
sum-=i*2-1;
}
cout<<sum<<'\n';
}
- C.Alternating Subsequence
思路:贪心,连续相同符号的值取最大。
AC代码:
void solve(ll sum=0) {
cin>>n;
ll x,y=0;
rep(i,n) {
cin>>x;
if(x*y>0) y=max(x,y);
else sum+=y,y=x;
}
cout<<sum+y<<'\n';
}
- D.Constant Palindrome Sum
思路:对于每一对值,存在一个范围,最终的x如果在这个范围里,那么对于这对值只需要修改一个数或不需要修改,反之两个数都需要修改。
这个范围D为:[min(x,y)+1,max(x,y)+k]
我们需要得到对于每一个可能的x,其在多少对元素的范围D内,所以可以想到区间修改,单点查询的解法。
因为是离线的,所以用差分数组+前缀和即可实现,我这里用了树状数组实现。
AC代码:
void solve(ll ans=0x3f3f3f3f) {
cin>>n>>k;
rep(i,2*k) sum[i]=c[i]=0;
rep(i,n/2) cin>>a[n/2-i+1];
rep(i,n/2) {
cin>>x;
int l=min(x,a[i])+1;
int r=max(x,a[i])+k;
sum[x+a[i]]++;
for(int j=l;j<=2*k;j+=j&-j) c[j]++;
for(int j=r+1;j<=2*k;j+=j&-j) c[j]--;
}
rep(i,2*k) {
ll t=0;
for(int j=i;j;j-=j&-j) t+=c[j];
ans=min(ans,n-t-sum[i]);
}
cout<<ans<<'\n';
}
- E.Weights Distributing
思路:预处理出每个点到a,b,c三点的距离(边数),可以想到一个好的方案一定是a到b和b到c间包含尽可能多的重复,在此基础上其余边尽可能少。
可以贪心的认为所有的重复路径一定与b点直接相连(i.e.最终方案一定是这样的:a走过几条边到某点d,经过重复利用的边集v进入b,再走过v到d,最后走到c)于是我们只需要枚举每个d点计算出max{dst(a to d)+dst(d to b)*2+dst(d to c)}即可。
AC代码:
const int N=4e5+5;
ll n,m,k,x,y,U,V,W;
ll sum[N],a[N],dst[4][N];
vector<int> ve[N];
void bfs(int V,int t) {
queue<int> que;
que.push(V),dst[t][V]=0;
while(!que.empty()) {
int u=que.front();
que.pop();
for(int v:ve[u]) if(dst[t][v]==-1) {
dst[t][v]=dst[t][u]+1;
que.push(v);
}
}
}
void solve(ll ans=1e18) {
cin>>n>>m>>U>>V>>W;
rep(j,3) rep(i,n) dst[j][i]=-1;
rep(i,n) ve[i].clear();
rep(i,m) cin>>a[i];
sort(a+1,a+m+1);
rep(i,m) sum[i]=sum[i-1]+a[i];
rep(i,m) {
cin>>x>>y;
ve[x].push_back(y);
ve[y].push_back(x);
}
bfs(V,1),bfs(U,2),bfs(W,3);
rep(i,n) {
ll d1=dst[1][i],d2=dst[2][i],d3=dst[3][i];
if(d1+d2+d3>m) continue;
ans=min(ans,sum[d1+d2+d3]+sum[d1]);
}
cout<<ans<<'\n';
}
- F.Restore the Permutation by Sorted Segments
思路:枚举待求排列的第一位数,即可推出后续所有元素。
具体实现:将每对l~r间所有元素存到集合中。每次删除每个集合中当前推出的元素,删除后哪个集合只剩下一个元素,这个元素就一定是原数组的下一个元素,如果出现多个或没有这样的元素,则说明方案不成立。
从思路上说其实比E更简单一些,只是代码写的稍有些乱。
AC代码:
bool check(int u) {
ans[1]=u;
rep(i,n) vis[i]=0;
for(int v:ve[u]) vis[v]=1;
rep(i,n-1) {
int cnt=0,t;
for(int v:ve[u]) {
b[v].erase(u);
if(b[v].size()==1) cnt++,t=v;
}
if(cnt^1) return false;
if(vis[t]&&num[t]!=i+1) return false;
u=ans[i+1]=*b[t].begin();
}
rep(i,n) {
if(i-1) cout<<' ';
cout<<ans[i];
}
cout<<endl;
return true;
}
void solve() {
cin>>n;
rep(i,n) ve[i].clear(),a[i].clear(),b[i].clear();
rep(i,n-1) {
cin>>num[i];
rep(j,num[i]) {
cin>>x;
ve[x].push_back(i);
a[i].insert(x);
}
}
rep(i,n) {
rep(j,n) b[j]=a[j];
if(check(i)) return ;
}
}