自己还是太菜了,五个小时一个题也没磕出来,还是队友牛逼!...

Primality Test

先看这个题,可能一上去就被\(\frac{f(x)+f(f(x))}{2}\)向下取整吓住了,但仔细想想,\(f(x)\)与\(f(f(x))\)不是相邻的质数吗?那么除2,向下取整,落点一定在两者之间,那么一定是合数。当然除了2,3的特例,这种特判下即可。

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. using namespace std;
  4. int main()
  5. {
  6. int T;cin>>T;
  7. while(T--)
  8. {
  9. ll x;cin>>x;
  10. if(x==1) cout<<"YES"<<endl;
  11. else cout<<"NO"<<endl;
  12. }
  13. return 0;
  14. }

Nun Heh Heh Aaaaaaaaaaa

为什么这么简单的题,我当初看了那么长的时间都没看出来。

发现这个题的主要难点在于以当前i(c[i]='h')为结尾的字串为nunhehheh的方案数。我们大胆的设状态,仔细考虑dp所代表的的集合,以及进行转移。设f[i][j]表示前i位,其中第i位匹配到nunhehheh的第j位的方案数。那么若c[i]=s[j],我们考虑枚举上一个字符也就是s[j-1]出现的位置,f[i][j]=f[k][j-1].其中c[k]==s[j-1].但这种方法显然是O(n^2)的。考虑转移时,其实f[k][j-1],只要是j-1即可,我们大可以用一个数组g[j]表示前i个字符中以某个点为j结尾的方案数。这样的话转移时就是O(1)的。

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. using namespace std;
  4. const int N=1e5+10,P=998244353;
  5. int T,n,f[N][12],cnt[N],g[12];
  6. char c[N];
  7. string s;
  8. inline ll power(ll x,ll y)
  9. {
  10. ll ans=1;
  11. while(y)
  12. {
  13. if(y&1) ans=ans*x%P;
  14. y>>=1;
  15. x=x*x%P;
  16. }
  17. return ans%P;
  18. }
  19. inline void clear()
  20. {
  21. for(int i=0;i<=n+1;++i)
  22. {
  23. cnt[i]=0;
  24. for(int j=0;j<=10;++j) f[i][j]=0;
  25. }
  26. memset(g,0,sizeof(g));
  27. }
  28. int main()
  29. {
  30. //freopen("1.in","r",stdin);
  31. scanf("%d",&T);
  32. s="2nunhehheh";
  33. while(T--)
  34. {
  35. scanf("%s",c+1);
  36. n=strlen(c+1);
  37. clear();
  38. for(int i=n;i>=1;--i)
  39. {
  40. cnt[i]=cnt[i+1];
  41. if(c[i]=='a') cnt[i]++;
  42. }
  43. ll ans=0;
  44. for(int i=1;i<=n;++i)
  45. {
  46. for(int j=9;j>=1;--j)//匹配nunhehheh的每一位。
  47. {
  48. if(c[i]==s[j])
  49. {
  50. f[i][j]=g[j-1];
  51. if(j==1) f[i][j]=1;
  52. g[j]=(g[j]+f[i][j])%P;
  53. if(j==9) ans=(ans+(ll)f[i][j]*(power(2,cnt[i])-1)%P)%P;
  54. }
  55. }
  56. }
  57. printf("%lld\n",ans);
  58. }
  59. return 0;
  60. }

Monopoly

哎呀,这个题,感觉自己当时的思路已经很接近了,但是还是输给了对于分类讨论的复杂性的恐惧,加上当时没有一个完整的思路进行支撑,就弃疗了。

首先我们设\(S\)为整个序列的和,\(s_i\)为第i位的前缀和,那么可以有这个式子\(s_i+kS=x(k>=0,1\leq i\leq n)\),其中只有\(S,x\)已知,我们做适当的调整,\(kS=x-s_i\),这样的话\(x-s_i\)必须是\(S\)的正整数(以及0)的倍数。那么可以想到\(x,s_i\)对\(S\)同余,也就是说余数相同。这样的话,我们可以对\(s_i\)根据对\(S\)的余数进行分类,每次询问的x只在模数相同的一类中找。接下来考虑最小化\(i+k*n\)的值,这个时候我们可以讨论S>0,那么为了满足倍数的关系,必须满足\(s_i\leq x\),同时为了k足够小,我们需要找到的\(s_i\)足够大,(你把这个过程放在数轴上想)。也就是小于等于x的最大值。这不是二分吗?我们再在每一类中进行排序,直接二分查找即可。接下来考虑S<0的情况,我们可以将序列中的每个数取反,再将x取反即可。注意当S=0时,系统会报错,单独讨论这种情况。

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. using namespace std;
  4. const int N=1e5+10;
  5. int T,n,m,num;
  6. map<ll,int>mp;//map表示序列号。
  7. map<ll,int>id[N];//余数为i的数字为j的最小的i。
  8. vector<ll>ve[N];
  9. ll s[N];
  10. inline void clear()
  11. {
  12. mp.clear();num=0;
  13. for(int i=0;i<=n;++i)
  14. {
  15. s[i]=0;
  16. id[i].clear();
  17. ve[i].clear();
  18. }
  19. }
  20. inline void solve()
  21. {
  22. mp[0]=0;
  23. for(int i=1;i<=n;++i)
  24. {
  25. if(mp.find(s[i])==mp.end())
  26. mp[s[i]]=i;
  27. }
  28. for(int i=1;i<=m;++i)
  29. {
  30. ll x;scanf("%lld",&x);
  31. if(mp.find(x)==mp.end()) puts("-1");
  32. else printf("%d\n",mp[x]);
  33. }
  34. }
  35. int main()
  36. {
  37. // freopen("1.in","r",stdin);
  38. scanf("%d",&T);
  39. while(T--)
  40. {
  41. scanf("%d%d",&n,&m);
  42. clear();
  43. for(int i=1;i<=n;++i)
  44. {
  45. ll x;scanf("%lld",&x);
  46. s[i]=s[i-1]+x;
  47. }
  48. if(s[n]==0) {solve();continue;}
  49. int op=1;if(s[n]<0) op=-1;
  50. ll S=s[n]*op;
  51. for(int i=0;i<=n;++i)
  52. {
  53. s[i]*=op;
  54. ll ps=(s[i]%S+S)%S;
  55. if(mp.find(ps)==mp.end()) mp[ps]=++num;
  56. if(id[mp[ps]].find(s[i])==id[mp[ps]].end())
  57. {
  58. id[mp[ps]][s[i]]=i;
  59. ve[mp[ps]].push_back(s[i]);
  60. }
  61. }
  62. for(int i=1;i<=num;++i) sort(ve[i].begin(),ve[i].end());
  63. for(int i=1;i<=m;++i)
  64. {
  65. ll x;scanf("%lld",&x);
  66. x*=op;
  67. if(x==0) {puts("0");continue;}
  68. ll ps=(x%S+S)%S;
  69. if(mp.find(ps)==mp.end()||ve[mp[ps]][0]>x) {puts("-1");continue;}
  70. int j=mp[ps];
  71. int k=upper_bound(ve[j].begin(),ve[j].end(),x)-ve[j].begin()-1;
  72. ll so=ve[j][k];
  73. ll ans=id[j][so]+((x-so)/S)*n;
  74. printf("%lld\n",ans);
  75. }
  76. }
  77. return 0;
  78. }

Jumping Monkey

这个题当初还是队友做出来的,自己主要是思路就没想到那一块去。由于每次从一个点出发一直跳,跳的最多的点的个数,想想其实问你的就是从这个点出发能跳到哪些点去,因为这些点我们可以按点权从小到大排序依次跳就完事了。当初困在了DP的思维上,想不出来好的转移方式。后来看了看题解大大的做法,其实是图论的知识吧,也不对就是思维的问题吧。我们考虑先将所有的点从小到大排序,依次考虑每个点能否到达点i,这样点i一定比之前的点权大,只要联通即可。也就是重新建图加边的问题。考虑当前是一个空白的图,我们依次将每个点加进去,考虑哪些点能到达当前这个点的话,就是连通块的问题,若当前点能够连到某个连通块,则这个连通块内的所有点都能到达这个点,那么这些点的答案都加1.然后将这个点及其连到的点合并成一个新的连通块即可。考虑整个需要我们维护的操作就是加上点,连通块整个的值加1,合并连通块。对于连通块的做法,我只会并查集,其实并查集就是维护连通块是否联通的问题,顺带记录一些信息。接下来就是这个题的精妙之处,我们发现,我们每次都是将一个连通块的值都加1,我们可以将加进去的点i当做根节点,这样的话那些连通块的深度就加1,符合我们的要求,我们只需要将点i向原本这个连通块的根节点连边即可。但这样不就打乱了原本的土的结构了吗?其实我们没必要维护原本的土的结构,我们还需要在一个点i加进去之后,那些连通块的点都可以加1就行。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1e5+10;
  4. int T,n,f[N],a[N],b[N],d[N];
  5. vector<int>son[N];
  6. vector<int>bian[N];
  7. map<pair<int,int>,bool>mp;
  8. inline void clear()
  9. {
  10. mp.clear();
  11. for(int i=1;i<=n;++i)
  12. {
  13. son[i].clear();
  14. bian[i].clear();
  15. f[i]=i;b[i]=i;
  16. d[i]=0;
  17. }
  18. }
  19. inline bool cmp(int x,int y) {return a[x]<a[y];}
  20. inline int getf(int x) {return f[x]==x?x:f[x]=getf(f[x]);}
  21. inline void dfs(int x)
  22. {
  23. for(auto y:bian[x])
  24. {
  25. d[y]=d[x]+1;
  26. dfs(y);
  27. }
  28. }
  29. int main()
  30. {
  31. // freopen("1.in","r",stdin);
  32. scanf("%d",&T);
  33. while(T--)
  34. {
  35. scanf("%d",&n);
  36. clear();
  37. for(int i=1;i<n;++i)
  38. {
  39. int x,y;scanf("%d%d",&x,&y);
  40. son[x].push_back(y);
  41. son[y].push_back(x);
  42. }
  43. for(int i=1;i<=n;++i) scanf("%d",&a[i]);
  44. sort(b+1,b+n+1,cmp);
  45. for(int i=1;i<=n;++i)//依次加入每一个点。
  46. {
  47. int x=b[i];
  48. for(auto y:son[x])//遍历i的每一个出边
  49. {
  50. if(a[y]>a[x]) continue;
  51. int t=getf(y);
  52. if(mp.find({x,t})==mp.end())
  53. {
  54. mp[{x,t}]=1;
  55. bian[x].push_back(t);
  56. f[t]=x;
  57. }
  58. }
  59. }
  60. d[b[n]]=1;
  61. dfs(b[n]);
  62. for(int i=1;i<=n;++i) printf("%d\n",d[i]);
  63. }
  64. return 0;
  65. }

Public Transport System

这个题当初也是想了好久没出来,显然因为如果\(a_i>a_{i-1}\)的话我们走当前的边权的值就为\(a_i-b_i\),这就使得我们必须记录当前点的上一条边是哪一条,想了想,其实所有的状态数也不多,即m个状态,毕竟只有m条边,每条边对应一个状态。但这个记录就只能用map实现,如果再跑dijkstra总的复杂度为O(mlognlogm),m的范围为\(1.2\times10^6\),这算出来,\(6\times10^9\),好吧,我觉得出题人肯定就是专门卡这个暴力的....

瞅瞅题解大大的做法,原来是要重新建图,又是建图的问题,这种题不是只在网络流中考察吗?好吧,这次确实拓了眼界。首先先将两种边分开,因为两种边权的图,毕竟没有单边权的方便。考虑边权\(a_i\)的边,我们没什么限制条件,但对于边权为\(a_i-b_i\)的边,我们必须满足一定条件,也就是前一条边的\(a_i\)必须小于当前的\(a_i\)。我们可以发现由于一个之前的边对应的可走的边权为\(a_i-b_i\)的边的\(a_i\)是在一个区间的,所以这就给我们的优化带来了可能。(不是我说的,是题解说的。)具体的,我们可以这样做:首先设d为当前点x的出度,我们将x拆分成d+1个点,\((x_0,x_1,x_2,...,x_d)\),让他们分别管理这些\(a_i-b_i\)出边,其中所有边权为\(a_i\)的边由\(x_0\)管理。我们将x所有边权为\(a_i-b_i\)的出边按照\(a_i\)的从大到小排序,接下来将他们依次交给\(x_i\)管理(也就是\(x_i\)连排过序后为i的边)。我们接下来i从0到d-1,从\(x_{i+1}\)向\(x_{i}\)连边权为0的边。这样由于提前排过序,能从小的\(a_i\)走\(a_i-b_i\)的特殊边,一定能从大的\(a_i\)走\(a_i-b_i\)的特殊边。之后我们只需要根据每个入边找到相对应能走的最大的\(x_i\),满足它所管理的边的\(a_i\)大于他的入边即可。

具体的如下图:





这样,一共的点为n+m,一共的边为2*m,即使m是\(1.2\times 10^6\)的数据量,我们跑dijkstra也完全没有问题。

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. using namespace std;
  4. const int N=4e5+10;
  5. int du[N],n,m,l[N],link[N],tot,vis[N];
  6. ll dis[N];
  7. //l[i]记录每个点拆出来的d+1个点的第一个点的编号。
  8. vector<int>son[N];//记录每个点,对应的边的编号。
  9. struct bian{int x,y,A,B;}b[N];
  10. struct wy{int y,v,next;}a[N<<1];
  11. priority_queue<pair<ll,int> >q;
  12. inline void add(int x,int y,int v)
  13. {
  14. a[++tot].y=y;a[tot].v=v;a[tot].next=link[x];link[x]=tot;
  15. }
  16. inline void clear()
  17. {
  18. tot=0;
  19. for(int i=1;i<=n+m;++i) link[i]=0;
  20. for(int i=1;i<=n;++i) son[i].clear(),du[i]=0;
  21. }
  22. inline bool cmp(int x,int y)
  23. {
  24. return b[x].A>b[y].A;
  25. }
  26. inline int find(int x,int A)//有一个ai的入边应该连向点x中的哪个点
  27. {
  28. if(!du[x]||A>=b[son[x][0]].A) return l[x];//返回x0的情况。
  29. int L=0,R=du[x]-1;
  30. while(L<R)//在x的边中查找>A的最小的边。
  31. {
  32. int mid=L+R+1>>1;
  33. if(b[son[x][mid]].A>A) L=mid;
  34. else R=mid-1;
  35. }
  36. return l[x]+L+1;
  37. }
  38. inline void dijkstra()
  39. {
  40. while(q.size()) q.pop();
  41. for(int i=1;i<=n+m;++i)
  42. {
  43. dis[i]=1e18;
  44. vis[i]=0;
  45. }
  46. dis[l[1]]=0;q.push({0,l[1]});
  47. while(!q.empty())
  48. {
  49. int x=q.top().second;q.pop();
  50. if(vis[x]) continue;
  51. vis[x]=1;
  52. for(int i=link[x];i;i=a[i].next)
  53. {
  54. int y=a[i].y;
  55. if(dis[x]+a[i].v<dis[y])
  56. {
  57. dis[y]=dis[x]+a[i].v;
  58. q.push({-dis[y],y});
  59. }
  60. }
  61. }
  62. }
  63. int main()
  64. {
  65. // freopen("1.in","r",stdin);
  66. int T;scanf("%d",&T);
  67. while(T--)
  68. {
  69. scanf("%d%d",&n,&m);
  70. clear();
  71. for(int i=1;i<=m;++i)
  72. {
  73. scanf("%d%d%d%d",&b[i].x,&b[i].y,&b[i].A,&b[i].B);
  74. son[b[i].x].push_back(i);
  75. du[b[i].x]++;
  76. }
  77. for(int i=1;i<=n;++i)
  78. {
  79. if(son[i].size())
  80. sort(son[i].begin(),son[i].end(),cmp);
  81. }
  82. int num=0;
  83. for(int i=1;i<=n;++i) //给每个点分配编号且处理内部的边。
  84. {
  85. l[i]=++num; //l[i] - l[i]+du[i]是这个点所有拆出来的点的编号。
  86. for(int j=du[i];j>=1;--j) add(l[i]+j,l[i]+j-1,0);
  87. num+=du[i];
  88. }
  89. for(int i=1;i<=n;++i)//处理点i的所有边
  90. {
  91. int js=son[i].size();
  92. for(int j=0;j<js;++j)//枚举i的所有出边
  93. {
  94. int id=son[i][j];
  95. int y=find(b[id].y,b[id].A);
  96. add(l[i],y,b[id].A);//先处理边权为ai的。
  97. add(l[i]+j+1,y,b[id].A-b[id].B);
  98. }
  99. }
  100. dijkstra();
  101. for(int i=1;i<=n;++i)
  102. {
  103. if(dis[l[i]]==1e18) dis[l[i]]=-1;
  104. printf("%lld",dis[l[i]]);
  105. if(i!=n) printf(" ");
  106. }
  107. printf("\n");
  108. }
  109. return 0;
  110. }

2021CCPC网络赛(重赛)题解的更多相关文章

  1. PTA|团体程序设计天梯赛-练习题目题解锦集(C/C++)(持续更新中……)

    PTA|团体程序设计天梯赛-练习题目题解锦集(持续更新中) 实现语言:C/C++:      欢迎各位看官交流讨论.指导题解错误:或者分享更快的方法!! 题目链接:https://pintia.cn/ ...

  2. 2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛 F Color it

    链接:https://www.nowcoder.com/acm/contest/163/F 来源:牛客网 2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛 F Color it 时间限制:C ...

  3. ACdream区域赛指导赛之专题赛系列(1)の数学专场

    Contest : ACdream区域赛指导赛之专题赛系列(1)の数学专场 A:EOF女神的相反数 题意:n(<=10^18)的数转化成2进制.翻转后(去掉前导零)输出十进制 思路:water ...

  4. 2018 ACMICPC上海大都会赛重现赛 H - A Simple Problem with Integers (线段树,循环节)

    2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛 H - A Simple Problem with Integers (线段树,循环节) 链接:https://ac.nowcoder.co ...

  5. 2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛 F Color it (扫描线)

    2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛 F Color it (扫描线) 链接:https://ac.nowcoder.com/acm/contest/163/F来源:牛客网 时间 ...

  6. 2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛 J Beautiful Numbers (数位DP)

    2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛 J Beautiful Numbers (数位DP) 链接:https://ac.nowcoder.com/acm/contest/163/ ...

  7. 2018 ICPC上海大都会赛重现赛 D Thinking-Bear magic (几何)

    2018 ACM 国际大学生程序设计竞赛上海大都会赛重现赛 D Thinking-Bear magic (几何) 链接:https://ac.nowcoder.com/acm/contest/163/ ...

  8. 2017南开ACM校赛(网络赛) 民间题解

    orz 首先说一下这个只是民间题解,可能会有很多错误 程序还没有评测,所以可能存在问题 C题比赛的时候没想到..后来发现是个模板题,所以没有代码 希望这份题解能对读者有所启发吧... A题 直接倒序枚 ...

  9. 2016 ACM-ICPC 青岛站网络赛G题 题解

    [参考博客][https://blog.csdn.net/Tawn0000/article/details/82255682] 题意: 将n个数按照每k个一组来合并,合并需要花费的cost是两个数的长 ...

随机推荐

  1. 技术栈:springboot2.x,vue,activiti5.22,mysql,带工作流系统

    前言 activiti工作流,企业erp.oa.hr.crm等审批系统轻松落地,请假审批demo从流程绘制到审批结束实例. 一.项目形式 springboot+vue+activiti集成了activ ...

  2. PyTorch学习笔记6--案例2:PyTorch神经网络(MNIST CNN)

    上一节中,我们使用autograd的包来定义模型并求导.本节中,我们将使用torch.nn包来构建神经网络. 一个nn.Module包含各个层和一个forward(input)方法,该方法返回outp ...

  3. (未完)Java集合框架梳理(基于JDK1.8)

    Java集合类主要由两个接口Collection和Map派生出来的,Collection派生出了三个子接口:List.Set.Queue(Java5新增的队列),因此Java集合大致也可分成List. ...

  4. Shell系列(20)- 字符截取命令cut

    前言 grep是按行提取:cut默认是通过制表符,按列提取,不能识别用空格作为分隔符 语法 cut [选项] [文件] 选项 -f :列号,截取哪几列数据,多个列号用逗号隔开:列与列之间默认用TAB制 ...

  5. 使用亚马逊服务器报错:Signature not yet current: 20190726T070253Z is still later than 20190726T070246Z (20190726T065746Z + 15 min.)时间不同步的解决办法

    1.首先获取亚马逊的时间: $ curl http://s3.amazonaws.com -v 2.更改当前服务器时间,使之与亚马逊时间同步 $ date -s 'xxxx-xx-xx xx:xx:x ...

  6. 鸿蒙内核源码分析(文件系统篇) | 用图书管理说文件系统 | 百篇博客分析OpenHarmony源码 | v63.01

    百篇博客系列篇.本篇为: v63.xx 鸿蒙内核源码分析(文件系统篇) | 用图书管理说文件系统 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一 ...

  7. P7115-[NOIP2020]移球游戏【构造】

    正题 题目链接:https://www.luogu.com.cn/problem/P7115 题目大意 \(n+1\)个柱子,前面\(n\)个上面各有\(m\)个球,球有\(n\)种颜色,每种\(m\ ...

  8. JavaScript 实现Sleep方法(多个setTimeout同步执行)

    前言 JavaScript是单线程的,如果所有操作都是同步,必将线程堵塞,页面失去响应.因此JavaScript采用了事件驱动机制,在单线程模型下,使用异步回调函数的方式来实现非阻塞的IO操作.因此也 ...

  9. python paramiko实现ssh上传下载执行命令

    paramiko ssh上传下载执行命令 序言 最近项目经常需要动态在跳板机上登录服务器进行部署环境,且服务器比较多,每次完成所有服务器到环境部署执行耗费大量时间.为了解决这个问题,根据所学的执行实现 ...

  10. The Data Way Vol.4|开源是创造软件诸多方法中最好的一种形式

    关于「The Data Way」 「The Data Way」是由 SphereEx 公司出品的一档播客节目.这里有开源.数据.技术的故事,同时我们关注开发者的工作日常,也讨论开发者的生活日常:我们聚 ...