时间机器(CDQ分治)
题解:第一眼瞄过去以为是个可持久化线段树(看来我还真菜),这题其实解法有很多,我就说说在考场上想到的CDQ分治。
首先这道题要按操作一步一步去做,而且操作还有时间的这个限制,一个操作i对另一操作j有贡献当且仅当该操作i在操作j前,且操作i的时间在操作j前,这是一个经典的二维偏序。我们把操作分成l,mid和mid+1,r两部分(这个按输入顺序分),先各自处理(l,mid)和(mid+1,r),然后处理l,mid对mid+1,r的贡献。
如何处理l,mid对mid+1,r的贡献呢,我们在此之前已经各自处理(l,mid)和(mid+1,r),即已把l,mid和mid+1,r按时间排好了,那么就进行一次类归并排序(按时间),用桶维护每个数出现的个数,按时间插入,that's ok。
然而记得桶要清空,而且不能从头清到尾,要插入了几个数就反向清空,不然时间会退化成n^2。
贴代码:
#include<algorithm> #include<fstream> #include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> using namespace std; int n,tot,ans[100050],cnt[100050],me[100050]; struct tedge { int id,t,x,num,ls; }d[100050],op[100050]; void CDQ(int l,int r) { if (l==r) return; int mid=(l+r)/2; CDQ(l,mid); CDQ(mid+1,r); int p1=l,p2=mid+1,p3=l; for (int i=1; i<=me[0]; i++) cnt[me[i]] = 0; me[0] = 0; while (p3<=r) { if (p1>mid||(p2<=r&&op[p2].t<op[p1].t)) { if (op[p2].id==3) ans[op[p2].num]+=cnt[op[p2].ls]; d[p3] = op[p2]; p2++; } else { if (op[p1].id==1) {cnt[op[p1].ls]++; me[0]++; me[me[0]]=op[p1].ls;} if (op[p1].id==2) {cnt[op[p1].ls]--; me[0]++; me[me[0]]=op[p1].ls;} d[p3] = op[p1]; p1++; } p3++; } for (int i=l; i<=r; i++) op[i] = d[i]; } bool cmp1(tedge a,tedge b) { return a.x<b.x; } bool cmp2(tedge a,tedge b) { return a.num<b.num; } int main() { freopen("c.in","r",stdin); freopen("c.out","w",stdout); scanf("%d",&n); for (int i=1; i<=n; i++) { scanf("%d%d%d",&op[i].id,&op[i].t,&op[i].x); op[i].num = i; } sort(op+1,op+1+n,cmp1); for (int i=1; i<=n; i++) { if (op[i].x==op[i-1].x) op[i].ls = op[i-1].ls; else { tot++; op[i].ls = tot; } } sort(op+1,op+1+n,cmp2); CDQ(1,n); sort(op+1,op+1+n,cmp2); for (int i=1; i<=n; i++) if (op[i].id==3) printf("%d\n",ans[op[i].num]); return 0; }