9.22考试 crf的数数 题解

  这道题当时第一反应是线段树,但没有继续想,因为当时打完第一题打算这道题和第二道题并列做,打完第二道题状压后时间还有两个小时多,先打完暴力再说,打完之后又接着去想,然后想了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 }
View Code

 最后总结一下这次考试,160分,一个烂大街的大众分,30个人23个前十,十多个160。一开始还以为我翻盘了,然后呵呵,只能说如果当时想起来莫队就好了……

转载于:https://www.cnblogs.com/liutianrui/p/7581951.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值