集訓隊作業2018:GAME(並查集)
阿新 • • 發佈:2018-12-21
題意:
題解:
把這個DP式子給列出來:
把這個字尾記為的話,每次就是,考慮對於初始的每個都維護一下,發現是條折線,維護一下這個折線,每次可以把小的一半合併到另一半去,於是就可以在的時間內做完了(感覺這道題可以做,難得想了)。
#include <bits/stdc++.h>
using namespace std;
const int RLEN=1<<18|1;
inline char nc() {
static char ibuf[RLEN],*ib,*ob;
(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
char ch=nc(); int i=0,f=1;
while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
return i*f;
}
const int N=1e6+50;
int n,Q,a[N],s[N],anc[N];
int k,b,l,r;
inline int ga(int x) {return (anc[x]==x) ? x : (anc[x]=ga(anc[x]));}
inline int merge(int x,int y) {anc[ ga(x)]=ga(y);}
inline int solve() {
if(a[n]>s[n-1]) return a[n]-s[n-1];
for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
int mx=2*a[n]-s[n];
for(int i=n-1;i>=1;i--)
mx=max(mx,-2*s[i]-mx);
return mx;
}
int main() {
n=rd();
for(int i=1;i<n;i++) a[i]=rd(), s[i]=s[i-1]+a[i];
for(int i=0;i<=s[n-1];i++) anc[i]=i;
l=0, r=s[n-1]; k=1,b=-s[n-1];
for(int i=n-2;~i;i--) {
int v=-s[i];
int x=(v-b)/k;
if(x<=l || x>=r) {
if(max(k*l+b,k*r+b)<=v) {
v=2*v-k*l-b;
k=-k; b=v-k*l;
}
continue;
}
if((x-l)<=(r-x)) {
if(k==-1) b=k*x+b-x, k=1;
for(int i=l;i<x;i++) merge(i,2*x-i);
l=x;
} else {
if(k==1) b=k*x+b+x, k=-1;
for(int i=x+1;i<=r;i++) merge(i,2*x-i);
r=x;
}
}
Q=rd();
while(Q--) {
int x=rd();
if(x>=s[n-1]) printf("%d ",x-s[n-1]);
else printf("%d ",k*ga(x)+b);
}
}