[BZOJ 3295][CQOI2011]动态逆序对
又一道坑了很久没有写题解的……
删除操作特别的棘手,但好在我们可以离线~时光倒流之后,删除就变成了添加。
每个点加进来之后,对答案的贡献可以分为两部分:
- 加入比他早,位置在他前面,值比他大的点。
- 加入比他早,位置在他后面,值比他小的点。
然后两者都可以用CDQ统计(我还偷懒用同一个CDQ做的)。
代码:
/************************************************************** Problem: 3295 User: danihao123 Language: C++ Result: Accepted Time:2332 ms Memory:5768 kb ****************************************************************/ #include <cstdio> #include <stack> #include <algorithm> using std::stack; using std::sort; const int maxn = 100005; typedef long long ll; struct Query { int id; int p, v; }; int n; ll C[maxn]; int lowbit(int x) { return x & (-x); } void add(int p, int v) { while(p <= n) { C[p] += v; p += lowbit(p); } } ll sum(int p) { ll ret = 0; while(p > 0) { ret += C[p]; p -= lowbit(p); } return ret; } int query(int l, int r) { return sum(r) - sum(l - 1); } void clean(int p) { while(p <= n && C[p] != 0) { C[p] = 0; p += lowbit(p); } } Query A[maxn], tmp[maxn]; ll ans[maxn]; void CDQ(int L, int R, bool flag) { if(L == R) { return; } int M = L + (R - L) / 2; CDQ(L, M, flag); CDQ(M + 1, R, flag); for(int p = L, l = L, r = M + 1; p <= R; p ++) { if(r > R || (l <= M && (flag ? A[l].p < A[r].p : A[l].p > A[r].p))) { tmp[p] = A[l]; add(A[l].v, 1); l ++; } else { tmp[p] = A[r]; ans[A[r].id] += query(A[r].v + 1, n); r ++; } } for(int i = L; i <= M; i ++) { clean(A[i].v); } for(int i = L; i <= R; i ++) { A[i] = tmp[i]; } } bool cmp_id(const Query& a, const Query& b) { return a.id < b.id; } int arr[maxn]; bool del[maxn]; int pos[maxn]; stack<int> delS; int main() { int m; scanf("%d%d", &n, &m); int id_siz = 0; for(int i = 1; i <= n; i ++) { scanf("%d", &arr[i]); } for(int i = 1; i <= m; i ++) { int a; scanf("%d", &a); delS.push(a); del[a] = true; } for(int i = 1; i <= n; i ++) { if(del[arr[i]]) { pos[arr[i]] = i; } else { id_siz ++; A[id_siz].id = id_siz; A[id_siz].p = i; A[id_siz].v = arr[i]; } } while(!delS.empty()) { int a = delS.top(); delS.pop(); id_siz ++; A[id_siz].id = id_siz; A[id_siz].p = pos[a]; A[id_siz].v = a; } CDQ(1, n, true); sort(A + 1, A + 1 + n, cmp_id); for(int i = 1; i <= n; i ++) { A[i].v = n - A[i].v + 1; } CDQ(1, n, false); for(int i = 1; i <= n; i ++) { ans[i] += ans[i - 1]; } for(int i = 0; i < m; i ++) { printf("%lld\n", ans[n - i]); } return 0; }
[BZOJ 1176][Balkan2007]Mokia
CDQ分治第一题……同时也是CDQ分治模板提。
感觉CDQ分治思路好秒啊……似乎在分治过程中做了个归并排序……
代码:
/************************************************************** Problem: 1176 User: danihao123 Language: C++ Result: Accepted Time:4364 ms Memory:17228 kb ****************************************************************/ #include <cstdio> #include <algorithm> using namespace std; const int maxw=2000005; const int maxq=10005; const int maxn=160001+4*maxq; int cnt=0; struct Query{ int ans_id; int x,y,d,v; Query(){} Query(int a,int b,int di,int ap){ x=a;y=b; d=di; ans_id=ap; } Query(int a,int b,int value){ x=a;y=b; v=value; d=0; } inline bool isQuery(){ return d!=0; } }; int w; int T[maxw]; inline int lowbit(int x){ return x&(-x); } inline void add(int p,int v){ while(p<=w){ T[p]+=v; p+=lowbit(p); } } inline int sum(int p){ register int x=0; while(p>0){ x+=T[p]; p-=lowbit(p); } return x; } inline void clear(int p){ while(p<=w && T[p]!=0){ T[p]=0; p+=lowbit(p); } } Query Q[maxn],tmp[maxn]; int ans[maxn]; void CDQ(int L,int R){ if(L==R){ return; } int M=L+(R-L)/2; CDQ(L,M); CDQ(M+1,R); int p,p1,p2; for(p=1,p1=L,p2=M+1;p<=(R-L+1);p++){ if((Q[p1].x<=Q[p2].x && p1<=M) || p2>R){ tmp[p]=Q[p1]; if(!Q[p1].isQuery()){ add(Q[p1].y,Q[p1].v); } p1++; }else{ tmp[p]=Q[p2]; if(Q[p2].isQuery()){ ans[Q[p2].ans_id]+=sum(Q[p2].y)*Q[p2].d; } p2++; } } for(p=1,p1=L;p<=(R-L+1);p++,p1++){ if(!Q[p1].isQuery()){ clear(Q[p1].y); } Q[p1]=tmp[p]; } } int main(){ int S,t,x,y,a,b; register int ans_cnt=0; scanf("%d%d",&S,&w); while(true){ scanf("%d",&t); if(t==3){ break; } scanf("%d%d%d",&x,&y,&a); // x++;y++; if(t==1){ Q[++cnt]=Query(x,y,a); }else{ scanf("%d",&b); // a++;b++; ans_cnt++; ans[ans_cnt]=(a-x+1)*(b-y+1)*S; Q[++cnt]=Query(a,b,1,ans_cnt); Q[++cnt]=Query(x-1,b,-1,ans_cnt); Q[++cnt]=Query(a,y-1,-1,ans_cnt); Q[++cnt]=Query(x-1,y-1,1,ans_cnt); } } CDQ(1,cnt); for(register int i=1;i<=ans_cnt;i++){ printf("%d\n",ans[i]); } return 0; }
[BZOJ 3155]Preprefix sum
蛮有意思的一题……
考虑询问[tex]SS_i[/tex],[tex][1,i][/tex]中的每个位置[tex]j[/tex]在答案中被计算的次数为[tex]i-j+1[/tex],然后式子就可以变成这样:
[tex]\Sigma_{j=1}^{i} A[j]*(i-j+1)[/tex]
[tex](i+1)*\Sigma_{j=1}^{i} A[j]-\Sigma_{j=1}^{i} A[j]*j[/tex]
这样问题就简单多了,开两个树状数组乱搞搞即可。
代码:
/************************************************************** Problem: 3155 User: danihao123 Language: C++ Result: Accepted Time:464 ms Memory:3164 kb ****************************************************************/ #include <cstdio> #include <cstdlib> const int maxn=100005; typedef long long ll; int n; ll A[maxn]; ll C_1[maxn]; // 正常的BIT inline int lowbit(int x){ return x&(-x); } inline void add(int p,ll v){ while(p<=n){ C_1[p]+=v; p+=lowbit(p); } } inline ll sum(int p){ register ll ans=0; while(p>0){ ans+=C_1[p]; p-=lowbit(p); } return ans; } ll C_2[maxn]; // 累积的BIT inline void add_2(int p,ll v){ register int pos=p; while(pos<=n){ C_2[pos]+=((ll)p)*v; pos+=lowbit(pos); } } inline ll sum_2(int p){ register ll ans=0; while(p>0){ ans+=C_2[p]; p-=lowbit(p); } return ans; } int main(){ int m,p; ll v; register int i; char *buf=(char*)calloc(10,1); scanf("%d%d",&n,&m); for(i=1;i<=n;i++){ scanf("%lld",&A[i]); add(i,A[i]); add_2(i,A[i]); } while(m--){ scanf("%s%d",buf,&p); if(buf[0]=='Q'){ printf("%lld\n",((ll)p+1LL)*sum(p)-sum_2(p)); }else{ scanf("%lld",&v); add(p,v-A[p]); add_2(p,v-A[p]); A[p]=v; } } free(buf); return 0; }
[BZOJ 3333]排队计划
传说中的大结论题TAT
假设每个数的后面小于它的数的个数为[tex]f[i][/tex](其位置为[tex]i[/tex]),那么逆序对数显然为[tex]\Sigma f[i][/tex]。
每次操作,只需要将涉及到的数的[tex]f[i][/tex]清为0即可。
为什么呢?任何数的后面比他小的数肯定也被一起拉出来排序了,所以这些数的[tex]f[i][/tex]要被清零。其他数为什么不收这个影响呢?因为这之后的比他小的位置,有的可能没被操作,有的可能被操作了。但是就算被操作了,放到后面位置的数照样会比这个数小(因为这个数如果在第一个选定位置的后面(在前面更是不受影响)还没被操作,肯定比那个第一个位置数还大)。
还有一个细节,一个被操作过的数不需要再被操作一遍,操作完了直接设为INF即可。
代码:
/************************************************************** Problem: 3333 User: danihao123 Language: C++ Result: Accepted Time:7920 ms Memory:24264 kb ****************************************************************/ #include <cstdio> #include <cctype> #include <utility> #include <algorithm> using namespace std; typedef unsigned long long ull; typedef pair<int,int> pii; const int maxn=500005; const int maxnode=maxn*4; const int INF=0x7fffffff; int A[maxn]; pii minv[maxnode]; inline void maintain(int o){ minv[o]=min(minv[o<<1],minv[o<<1|1]); } void build_tree(int o,int L,int R){ if(L==R){ minv[o]=make_pair(A[L],L); }else{ int M=L+(R-L)/2; build_tree(o<<1,L,M); build_tree(o<<1|1,M+1,R); maintain(o); } } int ql,qr; pii query_min(int o,int L,int R){ if(ql<=L && R<=qr){ return minv[o]; }else{ int M=L+(R-L)/2; pii ans=make_pair(INF,INF); if(ql<=M) ans=min(ans,query_min(o<<1,L,M)); if(qr>M) ans=min(ans,query_min(o<<1|1,M+1,R)); return ans; } } int p; void update(int o,int L,int R){ if(L==R){ minv[o].first=INF; }else{ int M=L+(R-L)/2; if(p<=M) update(o<<1,L,M); else update(o<<1|1,M+1,R); maintain(o); } } int lsh_siz; int C[maxn]; inline int lowbit(int x){ return x&(-x); } inline void add(int p){ while(p<=lsh_siz){ C[p]++; p+=lowbit(p); } } inline int sum(int p){ int ans=0; while(p>0){ ans+=C[p]; p-=lowbit(p); } return ans; } inline int readint(){ register int x; register char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); } return x; } int bf[21]; template<typename T> inline void putint(T x){ register int i,p=0; if(!x){ bf[p++]=0; }else{ while(x){ bf[p++]=x%10; x/=10; } } for(i=p-1;i>=0;i--) putchar(bf[i]+'0'); putchar('\n'); } int n; int A2[maxn],f[maxn]; int main(){ register int i,m,pos; register ull ans=0; pii temp; n=readint(); m=readint(); for(i=1;i<=n;i++){ A[i]=readint(); A2[i]=A[i]; } sort(A2+1,A2+1+n); lsh_siz=unique(A2+1,A2+1+n)-A2-1; for(i=n;i>=1;i--){ pos=lower_bound(A2+1,A2+1+lsh_siz,A[i])-A2; f[i]=sum(pos-1); ans+=f[i]; add(pos); } putint(ans); build_tree(1,1,n); while(m--){ pos=readint(); ql=pos; qr=n; while(true){ temp=query_min(1,1,n); if(temp.first>A[pos]) break; p=temp.second; ans-=f[p]; f[p]=0; update(1,1,n); } putint(ans); } return 0; }
[BZOJ 1901]Dynamic Rankings
终于A了!
做了4个月了,终于A了!
这个题其实不难。只要用树状数组维护主席树即可。不过请注意,此题的实际数据范围远比10000大!
代码:
/************************************************************** Problem: 1901 User: danihao123 Language: C++ Result: Accepted Time:540 ms Memory:32712 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; const int maxn=120051; const int maxlogn=31; const int maxnode=maxn*20; // ChairTree int sumv[maxnode]; int lc[maxnode],rc[maxnode]; int ct_cnt=0; int build_tree(int L,int R){ int ans=++ct_cnt; if(R>L){ int M=L+(R-L)/2; lc[ans]=build_tree(L,M); rc[ans]=build_tree(M+1,R); } return ans; } int p,v; int update(int o,int L,int R){ int ans=++ct_cnt; sumv[ans]=sumv[o]; lc[ans]=lc[o]; rc[ans]=rc[o]; if(R>L){ int M=L+(R-L)/2; if(p<=M) lc[ans]=update(lc[ans],L,M); else rc[ans]=update(rc[ans],M+1,R); } sumv[ans]+=v; return ans; } int LT_siz,RT_siz; int LT[maxlogn],RT[maxlogn]; int query(int L,int R,int k){ if(L==R) return L; int i; int LT_ans=0,RT_ans=0,the_ans,M=L+(R-L)/2; for(i=1;i<=LT_siz;i++) LT_ans+=sumv[lc[LT[i]]]; for(i=1;i<=RT_siz;i++) RT_ans+=sumv[lc[RT[i]]]; the_ans=RT_ans-LT_ans; if(k<=the_ans){ for(i=1;i<=LT_siz;i++) LT[i]=lc[LT[i]]; for(i=1;i<=RT_siz;i++) RT[i]=lc[RT[i]]; return query(L,M,k); }else{ for(i=1;i<=LT_siz;i++) LT[i]=rc[LT[i]]; for(i=1;i<=RT_siz;i++) RT[i]=rc[RT[i]]; return query(M+1,R,k-the_ans); } } int rt[maxn]; int siz; inline int lowbit(int x){ return x&(-x); } int n; void change(int pos,int value,int delta){ p=value; v=delta; while(pos<=n){ rt[pos]=update(rt[pos],1,siz); pos+=lowbit(pos); } } int get_ans(int l,int r,int k){ int temp=l-1; LT_siz=RT_siz=0; while(temp>0){ LT[++LT_siz]=rt[temp]; temp-=lowbit(temp); } while(r>0){ RT[++RT_siz]=rt[r]; r-=lowbit(r); } return query(1,siz,k); } int pre[maxn]; int A[maxn],A2[maxn]; int real_siz=0; void init_CT(){ register int i,pos; sort(A2+1,A2+1+real_siz); siz=unique(A2+1,A2+1+real_siz)-A2-1; rt[0]=build_tree(1,siz); for(i=1;i<=n;i++) rt[i]=rt[0]; for(i=1;i<=n;i++){ pos=lower_bound(A2+1,A2+1+siz,pre[i])-A2; change(i,pos,1); } } inline int get_lsh(int x){ return lower_bound(A2+1,A2+1+siz,x)-A2; } int Query[maxn][4]; int main(){ int m,u,v,d; char buf[3]; register int i; scanf("%d%d",&n,&m); for(i=1;i<=n;i++){ scanf("%d",&pre[i]); A2[++real_siz]=pre[i]; } for(i=1;i<=m;i++){ scanf("%s%d%d",buf,&u,&v); Query[i][1]=u; Query[i][2]=v; if(buf[0]=='C'){ Query[i][0]=1; A2[++real_siz]=v; }else{ Query[i][0]=0; scanf("%d",&Query[i][3]); } } init_CT(); for(i=1;i<=m;i++){ if(Query[i][0]){ change(Query[i][1],get_lsh(pre[Query[i][1]]),-1); pre[Query[i][1]]=Query[i][2]; change(Query[i][1],get_lsh(Query[i][2]),1); }else{ printf("%d\n",A2[get_ans(Query[i][1],Query[i][2],Query[i][3])]); } } return 0; }
[BZOJ 1878]HH的项链
扫描线处女作TAT
直接离线,按左端点排序扫描。每个点要保存后继相同节点,每种元素第一次出现的位置都加1。然后扫描的时候有后继就给后继加。之后求区间和即可。
代码:
/************************************************************** Problem: 1878 User: danihao123 Language: C++ Result: Accepted Time:1344 ms Memory:8444 kb ****************************************************************/ #include <cstdio> #include <cctype> #include <algorithm> using namespace std; const int maxn=50001,maxm=200001; int T[maxn]; int n; inline int lowbit(int x){ return x&(-x); } inline void update(int p,int v){ while(p<=n){ T[p]+=v; p+=lowbit(p); } } inline int query(int p){ register int ans=0; while(p>0){ ans+=T[p]; p-=lowbit(p); } return ans; } struct Query{ int l,r; int id; int ans; bool operator <(const Query& x) const{ return l==x.l?r<x.r:l<x.l; } }; Query Q[maxm]; bool cmp2(const Query& a,const Query& b){ return a.id<b.id; } // I/O优化 inline int readint(){ char c=getchar(); register int x=0; while(!isdigit(c)) c=getchar(); while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); } return x; } int bf[10]; inline void writeint(int x){ register int p=0; if(x==0){ bf[p++]=0; }else{ while(x){ bf[p++]=x%10; x/=10; } } for(register int i=p-1;i>=0;i--) putchar('0'+bf[i]); } int next[maxn]; int A[maxn]; int pos[1000001]; int main(){ int m,maxA=0; register int i,j; n=readint(); for(i=1;i<=n;i++){ A[i]=readint(); maxA=max(maxA,A[i]); } for(i=n;i;i--){ next[i]=pos[A[i]]; pos[A[i]]=i; } for(i=1;i<=maxA;i++) if(pos[i]) update(pos[i],1); m=readint(); for(i=1;i<=m;i++){ Q[i].l=readint(); Q[i].r=readint(); Q[i].id=i; } sort(Q+1,Q+1+m); register int tot=1; for(i=1;i<=m;i++){ while(tot<Q[i].l){ if(next[tot]) update(next[tot],1); tot++; } Q[i].ans=query(Q[i].r)-query(Q[i].l-1); } sort(Q+1,Q+1+m,cmp2); for(i=1;i<=m;i++){ writeint(Q[i].ans); putchar('\n'); } return 0; }