影片連結:C116 莫隊二次離線 P4887 莫隊二次離線_嗶哩嗶哩_bilibili
Luogu P4887 【模板】莫隊二次離線(第十四分塊(前體))
// 莫隊二次離線 O(n*sqrt(n)+n*C(k,14)) #include <iostream> #include <cstring> #include <algorithm> #include <cmath> #include <vector> using namespace std; const int N=100005; int n,m,k,B,block[N],a[N],t[N],pre[N]; long long ans[N]; vector<int> b; //二進位制中1的個數為k的數 struct F{ int id,l,r,z; }; vector<F> f[N]; //待求貢獻的資訊 struct Q{ int id,l,r; long long ans; bool operator<(Q &x){ if(block[l]!=block[x.l])return l<x.l; return r<x.r; } }q[N]; int count(int x){ //x的二進位制中1的個數 int s=0; while(x) s+=x&1,x>>=1; return s; } int main(){ scanf("%d%d%d",&n,&m,&k); B=sqrt(n); for(int i=1;i<=n;i++) scanf("%d",a+i),block[i]=(i-1)/B+1; for(int i=1,l,r;i<=m;i++) scanf("%d%d",&l,&r),q[i]={i,l,r}; sort(q+1,q+m+1); for(int i=0;i<1<<14;++i) if(count(i)==k)b.push_back(i); //最多C(7,14)=3432 for(int i=1;i<=n;i++){ pre[i]=t[a[i]];//a1~a[i-1]與ai異或有k個1的數的個數 for(auto x:b) ++t[a[i]^x]; } for(int i=1,l=1,r=0;i<=m;i++){ //莫隊 //r右移:f(l,x-1)=f(1,x-1)-f(1,l-1), x~[r+1,qr] if(r<q[i].r) f[l-1].push_back({i,r+1,q[i].r,-1}); while(r<q[i].r) q[i].ans+=pre[++r]; //r左移:-f(l,x-1)=-(f(1,x-1)-f(1,l-1)), x~[qr+1,r] if(r>q[i].r) f[l-1].push_back({i,q[i].r+1,r,1}); while(r>q[i].r) q[i].ans-=pre[r--]; //l左移:f(x+1,r)=f(1,r)-f(1,x), x~[ql,l-1] if(l>q[i].l) f[r].push_back({i,q[i].l,l-1,1}); while(l>q[i].l) q[i].ans-=pre[--l]+!k; //l右移:-f(x+1,r)=-(f(1,r)-f(1,x)), x~[l,ql-1] if(l<q[i].l) f[r].push_back({i,l,q[i].l-1,-1}); while(l<q[i].l) q[i].ans+=pre[l++]+!k; } //二次離線,累計f(1,l-1)和f(1,r)的貢獻 memset(t,0,sizeof(t)); for(int i=1,id,l,r,z;i<=n;i++){ //掃描ai for(auto& x:b) ++t[a[i]^x]; //與ai配對的數均+1 for(auto& y:f[i]){ //以ai為邊界的f for(int x=y.l;x<=y.r;x++) q[y.id].ans+=y.z*t[a[x]]; //累計配對貢獻 } } //後一個查詢是前一個查詢的增量,故求字首和 for(int i=2;i<=m;i++)q[i].ans+=q[i-1].ans; for(int i=1;i<=m;i++)ans[q[i].id]=q[i].ans; for(int i=1;i<=m;i++)printf("%lld\n",ans[i]); }
// 莫隊二次離線 O(n*sqrt(n)+n*C(k,14)) #include <iostream> #include <cstring> #include <algorithm> #include <cmath> #include <vector> #include <tuple> using namespace std; const int N=100005; int n,m,k,B,block[N],a[N],t[N],pre[N]; long long ans[N]; vector<int> b; //二進位制中1的個數為k的數 vector<tuple<int,int,int,int>> f[N]; //待求貢獻的資訊 struct Q{ int id,l,r; long long ans; bool operator<(Q &x){ if(block[l]!=block[x.l])return l<x.l; return r<x.r; } }q[N]; int main(){ scanf("%d%d%d",&n,&m,&k); B=sqrt(n); for(int i=1;i<=n;i++) scanf("%d",a+i),block[i]=(i-1)/B+1; for(int i=1,l,r;i<=m;i++) scanf("%d%d",&l,&r),q[i]={i,l,r}; sort(q+1,q+m+1); for(int i=0;i<1<<14;++i) //b內最多C(7,14)=3432 if(__builtin_popcount(i)==k)b.push_back(i); for(int i=1;i<=n;i++){ pre[i]=t[a[i]];//a1~a[i-1]與ai異或有k個1的數的個數 for(auto x:b) ++t[a[i]^x]; } for(int i=1,l=1,r=0;i<=m;i++){ //莫隊 //r右移:f(l,x-1)=f(1,x-1)-f(1,l-1), x~[r+1,qr] if(r<q[i].r) f[l-1].emplace_back(i,r+1,q[i].r,-1); while(r<q[i].r) q[i].ans+=pre[++r]; //r左移:-f(l,x-1)=-(f(1,x-1)-f(1,l-1)), x~[qr+1,r] if(r>q[i].r) f[l-1].emplace_back(i,q[i].r+1,r,1); while(r>q[i].r) q[i].ans-=pre[r--]; //l左移:f(x+1,r)=f(1,r)-f(1,x), x~[ql,l-1] if(l>q[i].l) f[r].emplace_back(i,q[i].l,l-1,1); while(l>q[i].l) q[i].ans-=pre[--l]+!k; //l右移:-f(x+1,r)=-(f(1,r)-f(1,x)), x~[l,ql-1] if(l<q[i].l) f[r].emplace_back(i,l,q[i].l-1,-1); while(l<q[i].l) q[i].ans+=pre[l++]+!k; } //二次離線,累計f(1,l-1)和f(1,r)的貢獻 memset(t,0,sizeof(t)); for(int i=1,id,l,r,z;i<=n;i++){ //掃描ai for(auto& x:b) ++t[a[i]^x]; //與ai配對的數均+1 for(auto& y:f[i]){ //以ai為邊界的f std::tie(id,l,r,z)=y; //元組的賦值 for(int x=l;x<=r;x++) //累計配對貢獻 q[id].ans+=z*t[a[x]]; } } //後一個查詢是前一個查詢的增量,故求字首和 for(int i=2;i<=m;i++)q[i].ans+=q[i-1].ans; for(int i=1;i<=m;i++)ans[q[i].id]=q[i].ans; for(int i=1;i<=m;i++)printf("%lld\n",ans[i]); }
P5047 [Ynoi2019 模擬賽] Yuno loves sqrt technology II - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)
P5501 [LnOI2019] 來者不拒,去者不追 - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)