HDU5213 Lucky(莫队+容斥)
这道题能看的出可以离线查询,不难想到莫队,但是寻常莫队每个询问只有一段,这样只需要排序即可
这里有两段,如果只按一段排序,那么复杂度显然得不到保证,以后不知道后面一段怎么样。
因此这里提出了一个想法就是,如果有多段,那么可以通过容斥原理来解决这个问题,把他变成多个询问的加减,这样复杂度就ok了
我们想要查询的是取两个数使他的和为k,从纸上画画就知道应该如何容斥。
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<cmath> #include<iostream> using namespace std; typedef long long ll; const int N=1e5+10; struct node{ int x1,x2; int y1,y2; ll ans; }q[N<<1]; struct Node{ int l,r; ll ans; int fa; int flag; }s[N<<1]; int a[N],k; int pos[N]; ll cnt[N]; ll res; bool cmp(Node a,Node b){ if(pos[a.l]==pos[b.l]) return a.r<b.r; return pos[a.l]<pos[b.l]; } void add(int x){ if(k-a[x]>0) res+=cnt[k-a[x]]; cnt[a[x]]++; } void sub(int x){ cnt[a[x]]--; if(k-a[x]>0) res-=cnt[k-a[x]]; } int main(){ int n; int i; while(cin>>n){ cin>>k; res=0; for(i=1;i<=n;i++) cnt[i]=0; int block=sqrt(n); for(i=1;i<=n;i++){ scanf("%d",&a[i]); pos[i]=(i-1)/block+1; } int m; cin>>m; int cnt=0; for(i=1;i<=m;i++){ scanf("%d%d%d%d",&q[i].x1,&q[i].y1,&q[i].x2,&q[i].y2); q[i].ans=0; cnt++; s[cnt].l=q[i].x1,s[cnt].r=q[i].y2,s[cnt].ans=0,s[cnt].fa=i,s[cnt].flag=1; cnt++; s[cnt].l=q[i].y1+1,s[cnt].r=q[i].x2-1,s[cnt].ans=0,s[cnt].fa=i,s[cnt].flag=1; cnt++; s[cnt].l=q[i].x1,s[cnt].r=q[i].x2-1,s[cnt].ans=0,s[cnt].fa=i,s[cnt].flag=0; cnt++; s[cnt].l=q[i].y1+1,s[cnt].r=q[i].y2,s[cnt].ans=0,s[cnt].fa=i,s[cnt].flag=0; } sort(s+1,s+1+cnt,cmp); int l=1; int r=0; for(i=1;i<=cnt;i++){ while(s[i].l<l) add(--l); while(s[i].l>l) sub(l++); while(s[i].r>r) add(++r); while(s[i].r<r) sub(r--); s[i].ans=res; int x=s[i].fa; int flag=s[i].flag; if(flag){ q[x].ans+=s[i].ans; } else{ q[x].ans-=s[i].ans; } } for(i=1;i<=m;i++) printf("%lld\n",q[i].ans); } }
没有人不辛苦,只有人不喊疼