这道题当时第一反应是线段树,但没有继续想,因为当时打完第一题打算这道题和第二道题并列做,打完第二道题状压后时间还有两个小时多,先打完暴力再说,打完之后又接着去想,然后想了5分多钟吧,扑街。
然后就发现这题似曾相识,有点像指针恒给我讲的分块“数颜色”,于是如获至宝的打了一个标准的分块。然后满心期待的以为至少能拿60分以上,结果被丝薄数据卡的一分没得,好无良的出题人啊。
考完试后发现好多人拿莫队打了70分,才反应过来这道题可以拿莫队打,然而之前只是听Q某犇讲过莫队的原理,具体实现也没打过,于是乎在自己的WW下打了一个J队……不得不说还挺像的……额挺像的……,然而我在宿舍里都排倒数,人家莫涛是国家队队长,差距啊……
其实正解不是莫队,莫队只是部分分,然而同桌Q某犇卡常数卡过去了,于是乎我也去打了莫队,然后各种恶意卡常,连计算机调内存,数组调用,O3等等等等我J某毕生所学都用上了,然而还是T成狗,不公平啊,我承认我没Q某犇帅,但脸黑到一定地步了啊!
于是乎,我只能乖乖的去打正解……
正解某种意义上挺有莫队的影子的,我们先把所有询问按照左右节点顺序排一遍序,然后从后向前进行查找,不断推进左节点位置(左节点一开始为n)然后维护一个t数组,代表该位置如果被选入会对当前答案做出如何的贡献,然后对于数x在该区间里从左到右出现的第x处利用差分的思想将该位置的t修改为1,对于出现的x+1处的t修改为-1,然后对于整个区间的贡献我们利用树状数组进行维护。然后直接对于该询问的右端点求前缀和即可。然而,我们应当注意几点,首先,我们在对树状数组进行修改时应当为修改值-t[当前针对的位置],然后就可以去掉之前改点对树状数组的影响,然后我们也应当记得把出现的x+2处的影响更新为0。
1 #pragma GCC optimze("O3") 2 #include<iostream> 3 #include<cstdlib> 4 #include<cstdio> 5 #include<cstring> 6 #include<algorithm> 7 #include<cmath> 8 #define N 1000005 9 #include<vector> 10 using namespace std; 11 int n,q,a[N],ans[N],t[N],b[N]; 12 vector<int> p[N]; 13 struct no 14 { 15 int l,r,bh; 16 }node[N]; 17 inline bool px(no a,no b) 18 { 19 if(a.l==b.l)return a.r<b.r; 20 return a.l<b.l; 21 } 22 inline int read() 23 { 24 int sum=0,f=1;char x=getchar(); 25 while(x<‘0‘||x>‘9‘){if(x==‘-‘)f=-1;x=getchar();} 26 while(x>=‘0‘&&x<=‘9‘){sum=(sum<<1)+(sum<<3)+x-‘0‘;x=getchar();} 27 return sum*f; 28 } 29 int lowbit(int x) 30 { 31 return x&(-x); 32 } 33 void add(int x,int z) 34 { 35 for(int i=x;i<=n;i+=lowbit(i)) 36 b[i]+=z; 37 } 38 int get(int x) 39 { 40 int ans=0; 41 for(int i=x;i>0;i-=lowbit(i)) 42 ans+=b[i]; 43 return ans; 44 } 45 int main() 46 { 47 48 scanf("%d%d",&n,&q); 49 for(int i=1;i<=n;i++) 50 a[i]=read();//scanf("%d",&a[i]); 51 for(int i=1;i<=q;i++) 52 { 53 node[i].bh=i; 54 node[i].l=read(),node[i].r=read(); //scanf("%d%d",&node[i].l,&node[i].r); 55 } 56 sort(node+1,node+1+q,px); 57 int l=n; 58 for(int i=q;i>=1;i--) 59 { 60 while(l>=node[i].l) 61 { 62 p[a[l]].push_back(l); 63 if(p[a[l]].size()>=a[l]) 64 { 65 int to=p[a[l]].size()-a[l]; 66 add(p[a[l]][to],1-t[p[a[l]][to]]);t[p[a[l]][to]]=1; 67 to--; 68 if(to>=0) 69 { 70 add(p[a[l]][to],-1-t[p[a[l]][to]]);t[p[a[l]][to]]=-1; 71 if(to>=1) 72 { 73 add(p[a[l]][to-1],0-t[p[a[l]][to-1]]);t[p[a[l]][to-1]]=0; 74 } 75 } 76 } 77 l--; 78 } 79 ans[node[i].bh]+=get(node[i].r); 80 } 81 for(int i=1;i<=q;i++) 82 printf("%d\n",ans[i]); 83 return 0; 84 }
最后总结一下这次考试,160分,一个烂大街的大众分,30个人23个前十,十多个160。一开始还以为我翻盘了,然后呵呵,只能说如果当时想起来莫队就好了……