2013 ACM/ICPC 长沙网络赛J题(二分)
题意:一个数列,给出这个数列中的某些位置的数,给出所有相邻的三个数字的和,数列头和尾处给出相邻两个数字的和。有若干次询问,每次问某一位置的数字的最大值。
分析:设数列为a1~an。首先通过相邻三个数字的和我们可以求出a3,a6,a9……是多少。a3=sum(a1,a2,a3)-sum(a1,a2)。a6=sum(a4,a5,a6)-sum(a3,a4,a5)。后面依次类推。
推到了数列的最右面,如果恰好知道了an或者a(n-1)中的一个,那么可以通过sum(an,a(n-1))减去它来求得另一个。
这个题还有个性质就是,只要知道数列中连续的两位就可以通过不断向两侧延伸的方法得到整个数列。因为任意相邻三个的和都知道,知道其中两个数自然可以得到第三个。
所以我们如果知道了最后两位则一定可以知道整个数列。同样如果根据已知的数列中的数和推出的数能得知两个已知的相邻的数的话,也可推出整个数列。
唯一一种无法确定该数列的情况就是我们,没有推出最后的两个数(即n%3==2的情况),且数列中已知给出的数也没有提供任何推出信息以外的信息。
这时我们就获得了一个不确定的数列,将数列中的数字ai按照i%3结果的不同分成三组,得0则已知。
得1的数之间互相是同增同减的,因为他们互相之间的差是一样的。a1-a4=sum(a1,a2,a3)-sum(a2,a3,a4)。得2的同理。所以这些同增同减的数字会同时取得最大值或最小值。
得1和得2的之间是此消彼涨的,因为他们互相之间的和是一样的。a1+a2=sum(a1,a2) a2+a4=sum(a2,a3,a4)-a3 a4+a5=sum(a3,a4,a5)-a3 所以得1的取得最小值则得2的取得最大值,反之亦然。
在符合了这些和与差的条件之后,唯一会导致数列不合法的情况就是出现负数。我们只需要先对得1的随意娶个值,并推出整个数列后,根据最小数值调整所有得1的取值,使最小的那个数恰好为0。
这样就可以使得1的取最小值,即让得2的取得最大值。
同理可以求得得1的最大值。
以上乃别人的思路,直接取一个值就行了!(没想到,我用二分做的)
//#pragma comment(linker, "/STACK:102400000") #include<cstdlib> #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<set> #include<map> #include<list> #include<queue> #include<vector> #define tree int o,int l,int r #define lson o<<1,l,mid #define rson o<<1|1,mid+1,r #define lo o<<1 #define ro o<<1|1 #define ULL unsigned long long #define LL long long #define inf 0x7fffffff #define eps 1e-7 #define N 100009 using namespace std; int m,n,T,t,x,y,u; int s[N],v[N],val[N]; int yes(int sub,int mid) { memcpy(val,v,sizeof(v)); val[sub]=mid; for(int i=sub+1; i<=n; i++) if(val[i]==-1) { val[i]=s[i-1]-val[i-1]-val[i-2]; if(val[i]<0) { return sub%3==i%3; } } for(int i=sub-1; i>=1; i--) if(val[i]==-1) { val[i]=s[i+1]-val[i+1]-val[i+2]; if(val[i]<0) { return sub%3==i%3; } } return 1; } int main() { #ifndef ONLINE_JUDGE freopen("ex.in","r",stdin); #endif int ncase=0; while(scanf("%d%*c",&n)==1) { for(int i=1; i<=n; i++) scanf("%d",&v[i]); for(int i=1; i<=n; i++) scanf("%d",&s[i]); v[0]=0; v[n+1]=0; for(int i=3; i<=n; i+=3) v[i]=s[i-1]-(s[i-2]-v[i-3]); for(int i=n-2; i>=1; i-=3) v[i]=s[i+1]-(s[i+2]-v[i+3]); int sub=-1; for(int i=0; i<=n; i++) if(v[i]!=-1&&v[i+1]!=-1) { sub=i; break; } if(sub!=-1) { for(int i=sub-1; i>=1; i--) v[i]=s[i+1]-v[i+1]-v[i+2]; for(int i=sub+2; i<=n; i++) v[i]=s[i-1]-v[i-1]-v[i-2]; } scanf("%d",&m); while(m--) { int x; scanf("%d",&x); x++; if(v[x]!=-1) printf("%d\n",v[x]); else { int l=0,r=s[x]; //v[x]的可行解不是从0开始的,是[l,r]这样的 while(l<r) { int mid=(l+r+1)>>1; if(yes(x,mid)) l=mid; else r=mid-1; } printf("%d\n",l); } } } return 0; }