CF1553D Backspace

题目传送门

题意简述:给定 \(s,t\),现在要依次输入 \(s\) 中的字符。对于每个字符 \(s_i\),你可以选择输入 \(s_i\) 或者使用退格键,判断是否能得到 \(t\)。

不妨将字符串反过来贪心考虑:设 \(i=|s|\),\(p=|t|\)。

  • 如果 \(s_i=t_p\),那么显然选择输入这个字符,\(p\gets p-1\),\(i\gets i-1\)。
  • 否则只能退格,不输入 \(s_i\) 并删除 \(s_{i-1}\),所以 \(i\gets i-2\)。

若 \(p=0\) 则能得到 \(t\),反之则不行。

也许有人会问,如果 \(s_{i-1}\) 也是退格怎么办?实际上若干个连续的退格等价于若干个不连续的退格:不妨设 \(s_{i-1}\) 的退格删了 \(s_j\),而 \(s_i\) 的退格删了 \(s_k\),显然有 \(k<j\)。也就是说 \(s_{k+1}\sim s_{j-1}\) 以及 \(s_{j+1}\sim s_{i-2}\) 全部被删掉了。因此它等价于在 \(s_j\) 处不输入而选择使用退格删除输入的 \(s_k\),在 \(s_i\) 出不输入而选择使用退格删除输入的 \(s_{i-1}\)。若 \(k+1=j\),那么刚好是我们想要的;而如果 \(k,j\) 不连续,那么可以递归证明下去。所以我们可以认为每次退格删除的必定是该位置的前一个字符,贪心算法的正确性就得到了证明。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. int main(){
  4. int T=1; cin>>T;
  5. while(T--){
  6. string s,t; cin>>s>>t;
  7. int p=t.size()-1;
  8. for(int i=s.size()-1;i>=0;i--)~p&&s[i]==t[p]?p--:i--;
  9. puts(~p?"NO":"YES");
  10. }
  11. return 0;
  12. }

CF1553F Pairwise Modulo

题目传送门

题意简述:给定长度为 \(n\) 的序列 \(a_i\),对于每个 \(k\in [1,n]\),求出 \(\sum_{1\leq i,j\leq k}a_i\bmod a_j\)。

每次考虑新加入的 \(a_i\) 对 \(a_j\ (1\leq j<i)\) 的影响,分成 \(a_i\bmod a_j\) 和 \(a_j\bmod a_i\) 来算。

  • \(a_i\bmod a_j\):

    • 若 \(a_j>a_i\),那么 \(a_j\) 的贡献为 \(a_i\)。
    • 若 \(a_j<a_i\),那么 \(a_j\) 的贡献为 \(a_i-a_j\times \lfloor\dfrac{a_i}{a_j}\rfloor\)。注意到前者与第一种情况的和为 \(a_i\times (i-1)\),而后面一部分可以在计算 \(a_j\) 时枚举 \(c\),对于每个 \([c\times a_j,(c+1)\times a_j-1]\) 区间加 \(c\times a_j\) 预处理出来。
  • \(a_j\bmod a_i\):
    • 若 \(a_j<a_i\),那么 \(a_j\) 的贡献为 \(a_j\)。
    • 若 \(a_j>a_i\),那么 \(a_j\) 的贡献为 \(a_j-a_i\times \lfloor\dfrac{a_j}{a_i}\rfloor\)。注意到前者与第一种情况的和为 \(\sum a_j\),而后面一部分可以枚举 \(c\),对于每个 \([c\times a_i,(c+1)\times a_i-1]\),将答案减去 \(a_j\) 落在该区间的数的个数 \(num\times (c\times a_i)\) 。

因为 \(a_i\) 互不相同,所以我们枚举的区间总数在 \(\sum\dfrac{m}{a_i}\approx m\ln m\)。而 区间加与单点求和 以及 单点加与区间求和 都是 BIT 的拿手好戏,因此总时间复杂度为 \(\mathcal{O}(n\log^2 n)\)。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define ll long long
  4. const int N=2e5+5;
  5. const ll M=3e5+5;
  6. ll n,sum,res;
  7. struct BIT{
  8. ll c[M];
  9. void add(ll x,ll v){while(x<M)c[x]+=v,x+=x&-x;}
  10. ll query(ll x){ll s=0; while(x)s+=c[x],x-=x&-x; return s;}
  11. }val,num;
  12. int main(){
  13. cin>>n;
  14. for(ll i=1,a;i<=n;i++){
  15. cin>>a,res+=sum+a*(i-1)-val.query(a);
  16. for(ll j=a;j<M;j+=a){
  17. int L=min(M-1,j+a-1);
  18. res-=j*(num.query(L)-num.query(j-1));
  19. val.add(j,j),val.add(L+1,-j);
  20. } num.add(a,1),sum+=a,cout<<res<<" ";
  21. }
  22. return 0;
  23. }

CF1553G Common Divisor Graph

题目传送门

题意简述:给出长度为 \(n\) 的序列 \(a_i\),两个点 \(i,j\) 之间有边当且仅当 \((a_i,a_j)>1\)。\(q\) 次询问给定 \(s,t\),你可以选择一个点 \(i\) 并新建节点 \(a_i\times (a_i+1)\),求至少要多少次操作才能使 \(s,t\) 联通。

不算难的一道题,但是赛时没写出来,与 2500+ 失之交臂/流泪。

首先,一个关键的性质是答案不超过 \(2\)。这很显然,因为 \(a_i(a_i+1)\) 一定能被 \(2\) 整除。所以 \((a_s(a_s+1),a_t(a_t+1))>1\)。

于是可以对三种情况分别讨论:

  • 答案为 \(0\):此时 \(s,t\) 在同一个连通块。连通块的预处理很容易:记 \(buc_{a_i}=i\),枚举每个质数 \(p\) 及其倍数 \(cp\),将所有 \(buc_{cp}\) 有值的全部连通即可,可以用并查集实现。
  • 答案为 \(1\):注意到 \(a_i+1\) 所含有的不同质因子是很少的,最多只有 \(7\),并且含有相同质因子的数一定在同一个连通块,这启发我们枚举每个 \(a_i\),求出 “\(a_i\) 所在的连通块” 与 “\(a_i+1\) 所含有的质因子所表示的所有连通块”,并将得到的所有连通块两两标记一下。那么若 \(s,t\) 所处的连通块之间有标记,则答案为 \(1\)。这个可以记录所有标记的连通块对(最多 \(21n\) 个),排序一边后每次询问使用 lower_bound 查找。
  • 否则答案为 \(2\)。

时间复杂度不太会分析,大概是 \(\mathcal{O}(m\log m+d^2n+q\log(d^2n))\),其中 \(m\) 为值域,\(d\) 为不超过值域的数所含有的最多质因子个数,本题中为 \(7\)。实际常数会很小,因为首先有一个 \(\dfrac{1}{2}\) 的常数,同时显然卡不满。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. #define pii pair <int,int>
  4. const int N=1e6+5;
  5. int n,q,a[N],buc[N],pr[N];
  6. int f[N],col[N],d[N],c;
  7. int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
  8. vector <pii> res;
  9. int main(){
  10. cin>>n>>q;
  11. for(int i=1;i<=n;i++)cin>>a[i],f[i]=i,buc[a[i]]=i;
  12. for(int i=2;i<N;i++){
  13. if(pr[i])continue;
  14. for(int j=i,pre=-1;j<N;j+=i){
  15. if(buc[j]){
  16. if(pre==-1)col[i]=find(buc[j]);
  17. else f[find(pre)]=find(buc[j]);
  18. pre=buc[j];
  19. } pr[j]=i;
  20. }
  21. }
  22. for(int i=2;i<N;i++)if(pr[i]==i)col[i]=find(col[i]);
  23. for(int i=1,p=a[1]+1;i<=n;p=a[++i]+1,c=0){
  24. d[++c]=find(i);
  25. while(p>1)d[++c]=col[pr[p]],p/=pr[p];
  26. sort(d+1,d+c+1),c=unique(d+1,d+c+1)-d-1;
  27. for(int i=1;i<c;i++)
  28. for(int j=i+1;j<=c;j++)
  29. res.push_back({d[i],d[j]});
  30. } sort(res.begin(),res.end());
  31. for(int i=1;i<=q;i++){
  32. int x,y; cin>>x>>y,x=find(x),y=find(y);
  33. if(x==y)puts("0");
  34. else{
  35. pii cur={min(x,y),max(x,y)};
  36. auto it=lower_bound(res.begin(),res.end(),cur);
  37. puts(it!=res.end()&&*it==cur?"1":"2");
  38. }
  39. }
  40. return 0;
  41. }

CF1553H XOR and Distance

题目传送门

题意简述:给出 \(n\) 个互不相同的 \(a_i\in [0,2^k)\),对于每个 \(x\in[0,2^k)\),求 \(\min_{i=1}^n\min_{j=i+1}^n|(a_i\oplus x)-(a_j\oplus x)|\)。

看到异或,想到维护一个 01-Trie。

首先考虑 \(x=0\) 的情况:为了查询最近的两个数之间的距离,我们对于每个节点 \(p\),我们首先想到维护落在该节点所表示区间的所有数的最小值和最大值,更新答案的方法显然。

但是,在 \(x\) 的变化过程中,我们需要翻转某一位(不妨设为第 \(i\) 位;位数从左到右从 \(k-1\) 到 \(0\) 标号)的值:由 \(0\) 变为 \(1\) 或者相反。对应到 Trie 上就是交换第 \(k-1-i\) 层所有节点的左右儿子(根节点为第 \(0\) 层)。但是,在交换左右儿子的时候,它们对应的最小值和最大值也会改变,这就意味着我们需要更新左右儿子的整颗子树所存储的信息,四舍五入相当于重构了整棵树:每一层的所有节点的信息都需要更新。这显然是无法承受的。

因此,现在需要一个更加高明的手段,使得交换左右子树时所有子节点信息不变,但仍能推出答案。方法已经呼之欲出了:不妨设节点 \(p\) 所表示的区间为 \([l_p,r_p]\),那么我们仅维护落在该区间的数距离 \(l_p\) 的最小值和最大值即可,即将原来的最小值和最大值减去 \(l_p\)。不难发现,在交换左右子树时,其内部节点的信息完全没有变化。此外,我们还需要记录每个节点所表示的区间长度 \(len_p=r_p-l_p+1\),方便更新信息。通过上述方法,我们可以将翻转第 \(i\) 位的时间复杂度降低到 \(2^{k-i}\)。

但这还是不够,因为 \(x\) 从 \(0\) 枚举到 \(2^k-1\) 的过程中,第 \(i\) 位需要翻转 \(2^{k-i}\) 次,那么第 \(i\) 位的总时间复杂度就是 \(4^{k-i}\),无法承受。可是我们完全不需要这样枚举:将第 \(i\) 位看成第 \(k-i-1\) 位!也就是从 \(0\) 到 \(2^k-1\) 枚举 \(x'\),那么真正的 \(x\) 就是 \(x'\) 在二进制下翻转后的十进制的值。这样一来,我们优化了 \(x\) 的枚举顺序,第 \(i\) 位仅需要翻转 \(2^i\) 次,从而使得第 \(i\) 位的时间复杂度就是 \(2^{k-i}\times 2^i=2^k\)。一共有 \(k\) 位,那么最终的总时间复杂度即为 \(\mathcal{O}(2^k\times k)\)。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. #define mem(x,v) memset(x,v,sizeof(x))
  4. const int M=1<<21;
  5. int n,k,R,node,ls[M],rs[M],mi[M],mx[M],len[M],ans[M],res[M];
  6. void push(int x){
  7. mi[x]=min(mi[ls[x]],mi[rs[x]]+len[x]/2);
  8. mx[x]=max(mx[ls[x]],mx[rs[x]]+len[x]/2);
  9. ans[x]=min(min(ans[ls[x]],ans[rs[x]]),mi[rs[x]]+len[x]/2-mx[ls[x]]);
  10. }
  11. void ins(int val,int bit,int &x){
  12. if(!x)len[x=++node]=1<<bit;
  13. if(!bit)return mi[x]=mx[x]=0,void();
  14. ins(val,bit-1,(val>>bit-1&1)?rs[x]:ls[x]),push(x);
  15. }
  16. void flip(int bit,int cur,int x){
  17. if(cur>bit)flip(bit,cur-1,ls[x]),flip(bit,cur-1,rs[x]),push(x);
  18. else swap(ls[x],rs[x]),push(x);
  19. }
  20. int main(){
  21. cin>>n>>k,mem(ans,0x3f),mem(mi,0x3f),mem(mx,0xcf);
  22. for(int i=1,a;i<=n;i++)cin>>a,ins(a,k,R); cout<<ans[R]<<" ";
  23. for(int i=1;i<1<<k;i++){
  24. int id=0;
  25. for(int j=0;j<k;j++){
  26. if((i-1>>k-j-1&1)!=(i>>k-j-1&1))flip(j,k-1,R);
  27. id+=(i>>j&1)<<(k-j-1);
  28. } res[id]=ans[R];
  29. } for(int i=1;i<1<<k;i++)cout<<res[i]<<" ";
  30. return 0;
  31. }

CF1553 部分题解的更多相关文章

  1. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  2. noip2016十连测题解

    以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...

  3. BZOJ-2561-最小生成树 题解(最小割)

    2561: 最小生成树(题解) Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1628  Solved: 786 传送门:http://www.lyd ...

  4. Codeforces Round #353 (Div. 2) ABCDE 题解 python

    Problems     # Name     A Infinite Sequence standard input/output 1 s, 256 MB    x3509 B Restoring P ...

  5. 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解

    题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...

  6. 2016ACM青岛区域赛题解

    A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  7. poj1399 hoj1037 Direct Visibility 题解 (宽搜)

    http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...

  8. 网络流n题 题解

    学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...

  9. CF100965C题解..

    求方程 \[ \begin{array}\\ \sum_{i=1}^n x_i & \equiv & a_1 \pmod{p} \\ \sum_{i=1}^n x_i^2 & ...

随机推荐

  1. 【UE4 C++ 基础知识】<7> 容器——TSet

    概述 TSet是一种快速容器类,(通常)用于在排序不重要的情况下存储唯一元素. TSet 类似于 TMap 和 TMultiMap,但有一个重要区别:TSet 是通过对元素求值的可覆盖函数,使用数据值 ...

  2. Coursera Deep Learning笔记 逻辑回归典型的训练过程

    Deep Learning 用逻辑回归训练图片的典型步骤. 笔记摘自:https://xienaoban.github.io/posts/59595.html 1. 处理数据 1.1 向量化(Vect ...

  3. Convolutional Neural Network-week1编程题(一步步搭建CNN模型)

    Convolutional Neural Networks: Step by Step implement convolutional (CONV) and pooling (POOL) layers ...

  4. Hadoop集群的配置(一)

    摘要: hadoop集群配置系列文档,是笔者在实验室真机环境实验后整理而得.以便随后工作所需,做以知识整理,另则与博客园朋友分享实验成果,因为笔者在学习初期,也遇到不少问题.但是网上一些文档大多互相抄 ...

  5. Manjaro安装Mariadb

    Mariadb是MySQL的一个复刻.由于MySQL被Oracle公司收购,MySQL的一些原始开发者担心MySQL会有开源方面的某些隐患,故领导开发了Mariadb. 如今,Mariadb已经作为许 ...

  6. Java版流媒体编解码和图像处理(JavaCPP+FFmpeg)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  7. Noip模拟41 2021.8.16

    T1 你相信引力吗 对于区间的大小关系问题,往往使用单调栈来解决 这道题的优弧和劣弧很烦,考虑将其等价的转化 由于所有的合法情况绕过的弧都不会经过最高的冰锥, 又因为环可以任意亲定起点,这样可以直接把 ...

  8. Netty:Netty的介绍以及它的核心组件(三)—— 事件和ChannelHandler

    Netty 使用异步事件驱动(Asynchronous Event-Driven)的应用程序范式,因此数据处理的管道(ChannelPipeLine)是经过处理程序(ChannelHandler)的事 ...

  9. 算法:拉丁方阵(Latin Square)

    拉丁方阵(英语:Latin square)是一种 n × n 的方阵,在这种 n × n 的方阵里,恰有 n 种不同的元素,每一种不同的元素在同一行或同一列里只出现一次.以下是两个拉丁方阵举例: 拉丁 ...

  10. 算法:九宫格问题--奇数阶魔方(Magic-Square)

    一.魔方介绍 魔方(这里是简称,也可以叫幻方.魔术矩阵,Magic Square)是 n×n 正方形网格(n 为每侧的单元数),里面每个单元格填充了不同的正整数 1, 2, 3, ... , n2,并 ...