BZOJ4262: Sum
Description
Input
第一行一个数 t,表示询问组数。
第一行一个数 t,表示询问组数。
接下来 t 行,每行四个数 l_1, r_1, l_2, r_2。
Output
一共 t 行,每行一个数 Sum。
Sample Input
4
1 3 5 7
2 4 6 8
1 1 9 9
9 9 1 1
1 3 5 7
2 4 6 8
1 1 9 9
9 9 1 1
Sample Output
9322587654
9025304064
1065645568
0
9025304064
1065645568
0
HINT
1<=t<=40000,1<=L1<=R1<=10^5,1<=L2<=R2<=10^5
因为数组是随机生成的,所以有一个经典的结论,固定起点或终点的线段的max-min期望有O(logn)种情况。
然后我们就可以把不相交的询问左右各分成logn段(每段max-min不同),然后用log^2n的时间内合并答案,具体见code中的solve(int l1,int r1,int l2,int r2)函数。
至于相交的询问可以容斥一下转化成不相交的询问和区间相同的询问,后者套一下线段树就可以了。
时间复杂度为O(Nlog^2N)-O(log^3N)。
#include<cstdio> #include<cctype> #include<queue> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i;i=next[i]) using namespace std; const int BufferSize=1<<16; char buffer[BufferSize],*head,*tail; inline char Getchar() { if(head==tail) { int l=fread(buffer,1,BufferSize,stdin); tail=(head=buffer)+l; } return *head++; } inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } typedef long long ll; const int maxn=200010; const int mod=1000000000; int A[maxn],Lmin[maxn],Rmin[maxn],Lmax[maxn],Rmax[maxn]; int S[maxn],Log[maxn],maxv[20][maxn],minv[20][maxn]; void init(int n) { Log[0]=-1; rep(i,1,n) Log[i]=Log[i>>1]+1,maxv[0][i]=minv[0][i]=A[i]; for(int j=1;(1<<j)<=n;j++) for(int i=1;i+(1<<j)-1<=n;i++) { maxv[j][i]=max(maxv[j-1][i],maxv[j-1][i+(1<<j-1)]); minv[j][i]=min(minv[j-1][i],minv[j-1][i+(1<<j-1)]); } } void query(int l,int r,int& mx,int& mn) { if(l>r) mx=-1,mn=2147483647; else { int k=Log[r-l+1]; mx=max(maxv[k][l],maxv[k][r-(1<<k)+1]); mn=min(minv[k][l],minv[k][r-(1<<k)+1]); } } struct Line { int l,r,mx,mn; }L1[maxn],L2[maxn]; ll solve(int l1,int r1,int l2,int r2) { if(l1>r1||l2>r2) return 0; int mx,mn;query(r1,l2,mx,mn);ll ans=0; int p,pmin,pmax,c1=0,c2=0; pmin=r1;pmax=r1;p=r1; while(p>=l1) { int nxt=max(l1-1,max(Lmin[pmin],Lmax[pmax])); L1[++c1]=(Line){nxt+1,p,A[pmax],A[pmin]}; p=nxt;if(A[p]>A[pmax]) pmax=p;else pmin=p; } pmin=l2;pmax=l2;p=l2; while(p<=r2) { int nxt=min(r2+1,min(Rmin[pmin],Rmax[pmax])); L2[++c2]=(Line){p,nxt-1,A[pmax],A[pmin]}; p=nxt;if(A[p]>A[pmax]) pmax=p;else pmin=p; } rep(i,1,c1) rep(j,1,c2) ans+=(ll)(L1[i].r-L1[i].l+1)*(L2[j].r-L2[j].l+1)*(max(mx,max(L1[i].mx,L2[j].mx))-min(mn,min(L1[i].mn,L2[j].mn))); return ans; } struct Node { int l,r;ll sumv; Node operator + (const Node& b) const { Node c;c.l=l;c.r=b.r; c.sumv=sumv+b.sumv+solve(l,r,b.l,b.r); return c; } }T[maxn<<2],ans; void build(int o,int l,int r) { if(l==r) T[o].l=T[o].r=l,T[o].sumv=0; else { int mid=l+r>>1,lc=o<<1,rc=lc|1; build(lc,l,mid);build(rc,mid+1,r); T[o]=T[lc]+T[rc]; } } int flag; void query(int o,int l,int r,int ql,int qr) { if(ql<=l&&r<=qr) { if(!flag) ans=T[o],flag=1; else ans=ans+T[o]; } else { int mid=l+r>>1,lc=o<<1,rc=lc|1; if(ql<=mid) query(lc,l,mid,ql,qr); if(qr>mid) query(rc,mid+1,r,ql,qr); } } int main() { ll xp1=1,xp2=1; rep(i,1,100000) { (xp1*=1023)%=mod; (xp2*=1025)%=mod; A[i]=xp1^xp2; } init(100000); int top=1;S[top]=0;A[0]=-1; rep(i,1,100000) { while(top&&A[i]<=A[S[top]]) top--; Lmin[i]=S[top];S[++top]=i; } top=1;S[top]=0;A[0]=2147483647; rep(i,1,100000) { while(top&&A[i]>=A[S[top]]) top--; Lmax[i]=S[top];S[++top]=i; } top=1;S[top]=100001;A[100001]=-1; dwn(i,100000,1) { while(top&&A[i]<=A[S[top]]) top--; Rmin[i]=S[top];S[++top]=i; } top=1;S[top]=100001;A[100001]=2147483647; dwn(i,100000,1) { while(top&&A[i]>=A[S[top]]) top--; Rmax[i]=S[top];S[++top]=i; } build(1,1,100000); int T=read(); while(T--) { int l1=read(),r1=read(),l2=read(),r2=read(); l2=max(l2,l1);r1=min(r1,r2);flag=0; if(r2<l1) puts("0"); else if(r1>=l2) { query(1,1,100000,l2,r1); printf("%lld\n",ans.sumv+solve(l1,r1,r1+1,r2)+solve(l1,l2-1,l2,r1)); } else printf("%lld\n",solve(l1,r1,l2,r2)); } return 0; }