首页 > 其他分享 >CF877F 题解

CF877F 题解

时间:2023-09-23 22:02:02浏览次数:32  
标签:ll int 题解 sum CF877F sqrt bl id

CF877F 题解

更好的阅读体验


提供一个扫描线 + 根号分治做法。
首先,可以把题目的条件转化成求 $sum_r-sum_{l-1}=k$ 的区间数。
考虑扫描线,当区间的右端点从 $r-1$ 移动到 $r$ 时,新增的区间的左端点就是所有满足 $sum_{l-1}=sum_r-k,l\le r$ 的 $l$。这时我们对 $sum_{l-1}$ 的出现次数按 $B$ 进行分治。
具体的,如果出现次数 $\le B$ ,那么我们这一次至多会修改 $B$ 个位置,而查询只有一次,于是可以采用 $O(1)-O(\sqrt{n})$ 的分块来维护;如果出现次数 $>B$,这样的数至多有 $\dfrac{n}{B}$ 个,我们可以对每个数都维护一个数据结构,要求支持区间加以及区间和,而每次加的操作只有 $O(1)$ 次,但是每个询问都要对每个数进行查询,共 $\dfrac{n}{B}$ 次,于是可以采用 $O(\sqrt{n})-O(1)$  的分块(具体可见

LOJ#6280. 数列分块入门 4)


总复杂度 $O(n\sqrt{n}+q\sqrt{n}+nB+q\dfrac{n}{B})$,$B$ 取 $\sqrt{n}$ 时就可以在 $O(n\sqrt{n}+q\sqrt{n})$ 的复杂度内解决这道题。

 

 1 const int N=100501,B=1000;
 2 int n,k,Q;
 3 int t[N],a[N],tot,bl[N],L[N],R[N];
 4 ll pre[N],ans[N],st[N];
 5 vector<pair<int,int> >qu[N];
 6 map<ll,int>mp;
 7 struct Brick{
 8     ll sum[N],sumb[N];
 9     inline void modify(int x){sum[x]++,sumb[bl[x]]++;}
10     inline ll query(int l,int r){
11         if(bl[l]==bl[r]){
12             ll ans=0;
13             for(int i=l;i<=r;i++)ans+=sum[i];
14             return ans;
15         }
16         ll ans=0;
17         for(int i=l;i<=R[bl[l]];i++)ans+=sum[i];
18         for(int i=bl[l]+1;i<bl[r];i++)ans+=sumb[i];
19         for(int i=L[bl[r]];i<=r;i++)ans+=sum[i];
20         return ans;
21     }
22 }T;
23 vector<int>hav[N],big;
24 int id[N],cnt;
25 struct Bri{
26     vector<ll>pre,sum,tag;
27     int n;
28     inline void init(int m){n=m;pre.resize(n+1),sum.resize(n+1),tag.resize(n+1);}
29     inline void add(int x,int k){
30         for(int i=x;i<=R[bl[x]];i++)pre[i]+=1ll*k*(i-x+1);
31         for(int i=bl[x]+1;i<=bl[n];i++)sum[i]+=1ll*k*(1-x),tag[i]+=k;
32     }
33     inline void add(int l,int r,int k){add(l,k);if(r<n)add(r+1,-k);}
34     inline ll query(int x){return pre[x]+sum[bl[x]]+1ll*tag[bl[x]]*x;}
35     inline ll query(int l,int r){return query(r)-query(l-1);}
36 }b[N/B+50];
37 int low[N/B+50][N],upp[N/B+50][N];
38 int main(){
39     n=read(),k=read();
40     for(int i=1;i<=n;i++)t[i]=read();
41     for(int i=1;i<=n;i++)a[i]=read(),pre[i]=pre[i-1]+(t[i]==1?a[i]:-a[i]);
42     for(int i=0;i<n;i++)st[++tot]=pre[i];
43     sort(st+1,st+tot+1);
44     tot=unique(st+1,st+tot+1)-st-1;
45     for(int i=1;i<=tot;i++)mp[st[i]]=i;
46     for(int i=0;i<n;i++)hav[mp[pre[i]]].push_back(i);
47     for(int i=1;i<=n;i++){
48         bl[i]=(i-1)/B+1;
49         if(bl[i]^bl[i-1])L[bl[i]]=i;
50         R[bl[i]]=i;
51     }
52     for(int i=1;i<=tot;i++)if((int)hav[i].size()>B)big.push_back(i),id[i]=++cnt;
53     for(int i=0;i<n;i++)if(id[mp[pre[i]]]){
54         int x=id[mp[pre[i]]];
55         low[x][i+1]++;
56         upp[x][i+2]++;
57     }
58     for(int i=1;i<=cnt;i++){
59         upp[i][0]=1;
60         for(int j=1;j<=n;j++)low[i][j]+=low[i][j-1],upp[i][j]+=upp[i][j-1];
61         b[i].init(low[i][n]);
62     }
63     Q=read();
64     for(int t=1;t<=Q;t++){
65         int l=read(),r=read();
66         qu[r].push_back({l,t});
67     }
68     for(int i=1;i<=n;i++){
69         ll cur=pre[i]-k;
70         if(mp[cur]){
71             int x=mp[cur];
72             int len=hav[x].size();
73             if(len<=B){
74                 for(auto j:hav[x]){
75                     if(j>=i)break;
76                     T.modify(j+1);
77                 }
78             }
79             else{
80                 int y=id[x];
81                 b[y].add(1,low[y][i],1);
82             }
83         }
84         for(auto j:qu[i]){
85             int l=j.first,id=j.second;
86             ans[id]+=T.query(l,i);
87             for(int k=1;k<=cnt;k++)ans[id]+=b[k].query(upp[k][l],low[k][i]);
88         }
89     }
90     for(int i=1;i<=Q;i++)write(ans[i]),putchar('\n');
91     return 0;
92 }

 

标签:ll,int,题解,sum,CF877F,sqrt,bl,id
From: https://www.cnblogs.com/Xttttr/p/17725161.html

相关文章

  • 【问题解决】shell脚本执行错误 $‘\r‘:command not found
    问题原因:在Windows中,换行符是由回车符(\r)和换行符(\n)组成的,而在Unix/Linux等系统中,只使用换行符(\n)作为换行标志。当你在Unix/Linux系统上运行一个包含Windows格式换行符的脚本时,Shell会尝试解释其中的回车符,导致错误提示$‘\r’:commandnotfound。这是因为Shell将回......
  • [COCI2016-2017#4] Osmosmjerka 题解
    [COCI2016-2017#4]Osmosmjerka题解我们发现对于每个点,只有八个方向,也就是说,最终能得到的字符串只会有\(8nm\)个,那我们可以考虑把这些字符串的哈希值求出来,相同的哈希值代表选到相同字符串的一种可能,直接统计即可。现在的问题就在于,怎么快速地求出这\(8nm\)个字符串的哈希......
  • 题解 CF1257G【Divisor Set】
    problem我们说一个集合\(D\)是一个好的集合,当不存在集合中的两个不同元素\(a,b\)使得\(a\)是\(b\)的约数。给定一个超大整数的素数表示形式\(N=\prod_{i=1}^n{p_i}\),要求从它的所有因子中选择尽可能多的元素组成一个好的集合。问这个最大的size是多少,输出模99824......
  • 题解 ARC165F【Make Adjacent】
    区间排序问题,主席树优化建图,最小字典序拓扑排序(priority_queue)problem给定一个长度为\(n*2\)的序列,其中每种元素恰好出现了2次。允许每次选择任意两个相邻的元素交换。那么必定存在一个最小\(k\):使得\(k\)次交换以后所有相同的元素都是相邻的。问恰好操作\(k\)次后,......
  • 【POJ 1521】Entropy 题解(贪心算法+优先队列+哈夫曼树)
    熵编码器是一种数据编码方法,通过对删除了“浪费”或“额外”信息的消息进行编码来实现无损数据压缩。换句话说,熵编码去除了最初不需要的信息,以准确编码消息。高度的熵意味着一条消息包含大量浪费的信息;以ASCII编码的英文文本是具有极高熵的消息类型的示例。已经压缩的消息,如JPEG图......
  • 题解 CF1873H Mad City
    题意描述马塞尔和瓦勒里乌(Valeriu)所在的疯狂城市由\(n\)栋建筑和\(n\)条双向道路组成。马塞尔和瓦勒里乌(Valeriu)分别从\(a\)号和\(b\)号建筑开始。马塞尔想赶上瓦勒里乌(换句话说,与他在同一栋楼里或在同一条路上相遇)。在每次移动过程中,他们都会选择前往当前建筑的邻近建......
  • 'main' attribute cannot be used in a module that contains top-level code 问题解
    核心是@main注解在main.swift文件中,可以重新命名下参考资料https://stackoverflow.com/questions/73431031/swift-cli-app-main-attribute-cannot-be-used-in-a-module-that-contains-top-leve......
  • CF1842F Tenzing and Tree 题解
    TenzingandTree感觉很典型的题,就是树的重心+绝对值等式解法:以每个点\(i\)为根分别\(bfs\),得到一个距离数组\(dis\),取前\(k\)个值的权值为和,更新\(w[k]\)的值,\(n\)个点分别为根,更新\(n\)遍之后,得到\(w\)数组,则\((n-1)\timesi-w[i]\),即为\(i\)个点时候的......
  • 砝码称重 题解
    砝码称重题解前言这道题时限完全可以开到1s,空间也开不到1024kb白想那么多优化(不过这个复杂度可能是目前来看最合理(算出来保证能过)的。题意简述有一个长度为\(n\)的序列\(a\),有两种操作:把\(l\)到\(r\)的所有数改为\(x\);查询用\(l\)到\(r\)的所有数(每个数可......
  • 题解 AtCoder Beginner Contest 267 A~H
    ABC267solutionhttps://atcoder.jp/contests/abc267/ProblemA.Saturday题目描述输入一个表示星期的英文字符串,输出:还有多少天到星期六?solution依题意模拟。\(O(1)\)。ProblemB.Split?题目描述Robin有十个小球,如图排列成这样,然后Robin将一些球击飞了,他告诉你哪些......