1. 程式人生 > >集訓隊作業2018:GAME(並查集)

集訓隊作業2018:GAME(並查集)

題意:
在這裡插入圖片描述

題解:
把這個DP式子給列出來:
fi=si+maxj{2ajsjfj}f_i = s_i + \max_j \{ 2*a_j-s_j- f_j\}

把這個字尾max\max記為mm的話,每次就是m=max{m,2si1m}m=\max\{m,-2*s_{i-1}-m\},考慮對於初始的每個mm都維護一下,發現是條折線,維護一下這個折線,每次可以把小的一半合併到另一半去,於是就可以在O(sum)O(|sum|)的時間內做完了(感覺這道題可以O

(nlogn)O(n \log n)做,難得想了)。

#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); } }