標籤:space font 線段 namespace tchar ios define void 連結
題目連結:http://codeforces.com/contest/981/problem/G
題目大意:
有n個初始為空白的‘魔法’可重集,向一個‘可重集’加入元素時,若該元素未出現過,則將其加入;否則該可重集中所有元素的個數都會翻倍。
例如將$2$加入${1,3}$會得到${1,2,3}$,將$2$加入${1,2,3,3}$會得到${1,1,2,2,3,3,3,3}$.
$q$次操作,每次操作要麼向一個區間內的所有可重集加入某個元素,要麼詢問一個區間內可重集的大小之和。
$n,q ≤ 2×10^5$
題解:
發現對於出現過該元素的區間就是區間乘,沒有出現過的就是區間加
操作1我們先把$[l,r]$全部乘上2,再把之前已經出現過當前元素的區間乘上2的逆元再+1,最後合并一下左右區間就好了。合并大概就是保證這個操作時間複雜度的關鍵了,只是我也不知道怎麼算
發現這樣我們要維護每個元素出現的區間,同時支援方便的合并,我們開n個set就好了
正是做完這題我發現我竟然不會寫區間乘法線段樹
#include<algorithm>#include<cstring>#include<cstdio>#include<iostream>#include<set>#define pa pair<int,int>#define mid ((l+r)>>1)using namespace std;typedef long long ll;const int N=2e5+15;const int mod=998244353;ll n,q,inv;ll sum[N<<2],add[N<<2],mul[N<<2];set <pa> st[N]; inline ll read(){ char ch=getchar(); ll s=0,f=1; while (ch<‘0‘||ch>‘9‘) {if (ch==‘-‘) f=-1;ch=getchar();} while (ch>=‘0‘&&ch<=‘9‘) {s=(s<<3)+(s<<1)+ch-‘0‘;ch=getchar();} return s*f;}ll qpow(ll a,ll b){ ll re=1; for (;b;b>>=1,a=a*a%mod) if (b&1) re=re*a%mod; return re;}void build(int o,int l,int r){ mul[o]=1;add[o]=sum[o]=0; if (l==r) return; build(o<<1,l,mid); build(o<<1|1,mid+1,r);}void pushup(int o,int l,int r){ //sum[o]=sum[o<<1]+sum[o<<1|1]; sum[o]=(sum[o<<1]*mul[o<<1]+add[o<<1]*(mid-l+1))%mod; sum[o]=(sum[o]+sum[o<<1|1]*mul[o<<1|1]+add[o<<1|1]*(r-mid))%mod;}void pushdown(int o,int l,int r){ if (mul[o]!=1) { ll p=mul[o]; mul[o]=1; //(sum[o<<1]*=p)%=mod; //(sum[o<<1|1]*=p)%=mod; (add[o<<1]*=p)%=mod; (add[o<<1|1]*=p)%=mod; (mul[o<<1]*=p)%=mod; (mul[o<<1|1]*=p)%=mod; } if (add[o]!=0) { ll p=add[o]; add[o]=0; // (sum[o<<1]+=p*(mid-l+1))%=mod; // (sum[o<<1|1]+=p*(r-mid))%=mod; (add[o<<1]+=p)%=mod; (add[o<<1|1]+=p)%=mod; }}void update(int o,int l,int r,int x,int y,ll z,int flag){ if (l>=x&&r<=y) { if (flag==1) { (mul[o]*=z)%=mod; (add[o]*=z)%=mod; // (sum[o]*=z)%=mod; } if (flag==2) { (add[o]+=z)%=mod; // (sum[o]+=(r-l+1)*z)%=mod; } return; } pushdown(o,l,r); if (x<=mid) update(o<<1,l,mid,x,y,z,flag); if (y>mid) update(o<<1|1,mid+1,r,x,y,z,flag); pushup(o,l,r);} void merge(int x,int L,int R){ set<pa>::iterator it; it=st[x].lower_bound(pa(L,L)); for (;it!=st[x].end();it++) { set<pa>::iterator lst=it;lst--; int l=(*lst).second+1; int r=(*it).first-1; int upl=max(L,l); int upr=min(R,r); if (upr>=upl) { update(1,1,n,upl,upr,inv,1); update(1,1,n,upl,upr,1,2); } if ((*it).first>=R) break; } int mergeL=L,mergeR=R; it=st[x].upper_bound(pa(L,L));it--; if ((*it).second>=mergeL) mergeL=(*it).first; it=st[x].upper_bound(pa(R,R));it--; if ((*it).second>=mergeR) mergeR=(*it).second; vector <pa> er; it=st[x].lower_bound(pa(mergeL,mergeL)); for (;it!=st[x].end();it++) { pa e=*it; if (e.first>=mergeL&&e.second<=mergeR) er.push_back(e); else break; } for (int i=0;i<er.size();i++) st[x].erase(er[i]); st[x].insert(pa(mergeL,mergeR));}ll query(int o,int l,int r,int x,int y){ if (l>=x&&r<=y) return (sum[o]*mul[o]+add[o]*(r-l+1))%mod;// if (l>=x&&r<=y) return sum[o]%mod; pushdown(o,l,r); ll re=0; if (x<=mid) (re+=query(o<<1,l,mid,x,y))%=mod; if (y>mid) (re+=query(o<<1|1,mid+1,r,x,y))%=mod; pushup(o,l,r); return re; }int main(){ inv=qpow(2,mod-2); //inv=(mod+1)/2; n=read();q=read(); for (int i=0;i<=n;i++) { st[i].insert(pa(0,0)); st[i].insert(pa(n+1,n+1)); } build(1,1,n); while (q--) { int opt=read(); if (opt==1) { int l=read(),r=read(),z=read(); update(1,1,n,l,r,2,1); merge(z,l,r); } if (opt==2) { int l=read(),r=read(); printf("%lld\n",query(1,1,n,l,r)); } } return 0;}
[Avito Code Challenge 2018 G] Magic multisets(線段樹)