来补坑了……

个人认为三道题难度差不多……

还有要说一嘴,为啥我在其他网站代码都好好的,复制到 cnblogs 上 Tab 就成 8 空格了?不过也懒得改了。


T1 序列

首先,遇到这种加一减一还带附加条件的基本都是图论题,所以我们用图论的思维去想这道题。将每个 \(a_i\) 看成一个点,并把每个点赋一个新的权值 \(b_i-a_i\),这样最终就是问是否可以把每个点权变为 \(0\)。

先考虑操作二,对每个操作二的点连无向边建图,同一连通块的点可以互相在总和不变的情况下改变为任意值(因为操作二一个加 \(1\) 一个减 \(1\) 是具有传递性的),于是我们可以把连通块用并查集缩点,当成一个权值不变的点看待。

再考虑操作一,在之前缩点的图上对每个操作一连无向边,建好的图必是二分图或非二分图的情况之一。对于二分图,我们可以在左部点与右部点总和之差不变的前提下修改点权,那要保证答案为YES则必然要两边相等;对于非二分图,我们仍从二分图的角度去考虑,相当于左部点或右部点内部出现了连边,那么此时其便无法增加或减少奇数值,只能增减偶数值,进而整幅图的总和都无法增减奇数值。那么要保证答案为YES只能是总和为偶数。

单次复杂度 \(O(n)\)。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const int N=1e5+7;
  5. int n,m,cnt,a[N],b[N],p[N],q[N],fa[N];
  6. bool vis[N];
  7. ll s[N],c[3];
  8. vector<int> G[N];
  9. int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);}
  10. bool dfs(int u,int col)
  11. {
  12. vis[u]=col,c[col]+=s[u];
  13. bool fl=1;
  14. for(int i=0,v;i<G[u].size();++i)
  15. {
  16. if(c[u]==c[v=G[u][i]]) fl=0;
  17. if(!vis[v]&&!dfs(v,3-col)) fl=0;
  18. }
  19. return fl;
  20. }
  21. int main()
  22. {
  23. int T; scanf("%d",&T);
  24. while(T--)
  25. {
  26. scanf("%d%d",&n,&m); cnt=0;
  27. for(int i=1;i<=n;++i) fa[i]=i,G[i].clear(),scanf("%d",a+i);
  28. for(int i=1;i<=n;++i) s[i]=vis[i]=0,scanf("%d",b+i);
  29. for(int i=1,op,x,y;i<=m;++i)
  30. {
  31. scanf("%d%d%d",&op,&x,&y);
  32. if(op==2) fa[find(x)]=find(y);
  33. else p[++cnt]=x,q[cnt]=y;
  34. }
  35. for(int i=1;i<=n;++i) s[find(i)]+=b[i]-a[i];
  36. for(int i=1;i<=cnt;++i)
  37. {
  38. int u=find(p[i]),v=find(q[i]);
  39. G[u].push_back(v);
  40. G[v].push_back(u);
  41. }
  42. bool ok=1;
  43. for(int i=1;i<=n;++i)
  44. if(find(i)==i&&!vis[i])
  45. {
  46. c[1]=c[2]=0;
  47. bool fl=dfs(i,1);
  48. if(fl&&c[1]!=c[2]) {ok=0; break;}
  49. if(!fl&&((c[1]+c[2])&1)) {ok=0; break;}
  50. }
  51. puts(ok?"YES":"NO");
  52. }
  53. return 0;
  54. }

T2 冒泡排序

首先来盘一盘冒泡排序的本质,手玩了几次冒泡排序之后可以很容易发现:一轮冒泡排序就是把每个数的逆序对数减一(如果为零则不用减)。那么我们这道题实际上就是给你一个序列要支持以下操作:单点增减(交换操作实际上只影响了一个点的逆序对个数),全局增减(如果是 \(0\) 则忽略),以及求全局和。忽略 \(0\) 这一个好像不太好搞,但是仍可以用线段树做,比较麻烦,这里不讲。

止步于此,没想出来,遂去无耻地看了题解。其实我们可以用时间为下标维护逆序对个数。具体来说,建立一个树状数组,在 1 号点插入初始序列的逆序对总数,然后再往后的第 \(i+1\) 号点代表了“第 \(i\) 轮冒泡排序后序列的逆序对总数”,这个可以用桶差分 \(O(n\log n)\) 预处理,我们记cnt[i]为“逆序对个数等于 \(i\) 的数的个数”(比较拗口,别咬到舌头了),那么每一轮(假设为第 \(i\) 轮)要减去的逆序对个数就是所有“逆序对个数大于 \(i\) 的数的个数”(还是比较拗口)。对于询问操作,直接求第 \(k\) 轮后的树状数组前缀和即可。

现在来考虑交换操作。分类讨论,假设要交换 \(a_x\) 与 \(a_{x+1}\),如果交换后 \(a_x<a_{x+1}\),那么实际上初始序列的逆序对个数便要减一,那么这个减一的贡献什么时候消失呢?显然是在第 \(b_{x}+2\) 轮(这里b[]是记录每个数逆序对个数的数组,\(b_x\) 是已经交换且减一的值),在这里对应的树状数组上把贡献加回来即可。对于 \(a_x>a_{x+1}\) 可采用同样的逻辑分析,这里不再赘述。

复杂度 \(O(n\log n)\)。

  1. #include <bits/stdc++.h>
  2. #define lb(x) (x&(-x))
  3. using namespace std;
  4. typedef long long ll;
  5. const int N=2e5+5;
  6. int n,m,a[N],b[N],cnt[N];
  7. ll tot,c[N];
  8. void add(int x,ll k) {for(;x<=n;x+=lb(x)) c[x]+=k;}
  9. ll ask(int x) {ll res=0; for(;x;x-=lb(x)) res+=c[x]; return res;}
  10. int main()
  11. {
  12. scanf("%d%d",&n,&m);
  13. for(int i=1;i<=n;++i)
  14. {
  15. scanf("%d",a+i);
  16. b[i]=i-1-ask(a[i]);
  17. tot+=b[i],++cnt[b[i]];
  18. add(a[i],1);
  19. }
  20. memset(c,0,sizeof(c));
  21. add(1,tot); tot=0;
  22. for(int i=1;i<=n;++i)
  23. {
  24. tot+=cnt[i-1];
  25. add(i+1,-(n-tot));
  26. }
  27. while(m--)
  28. {
  29. int op,x; scanf("%d%d",&op,&x);
  30. x=min(x,n-1);
  31. if(op==1)
  32. {
  33. swap(a[x],a[x+1]);
  34. swap(b[x],b[x+1]);
  35. if(a[x]<a[x+1])
  36. {
  37. --b[x];
  38. add(1,-1);
  39. add(b[x]+2,1);
  40. }
  41. else
  42. {
  43. ++b[x+1];
  44. add(1,1);
  45. add(b[x+1]+1,-1);
  46. }
  47. }
  48. else printf("%lld\n",ask(x+1));
  49. }
  50. return 0;
  51. }

T3 最小环

此题不少人说简单,这只是这道题贪心思路好想却不好证所带来的错觉。(也有可能是我真的菜

首先这道题其实是把一堆数分成若干个环(对于每一个 \(k\),有 \(\gcd(n,k)\) 个环,不知道的话可以做 luogu5887 学习一下)。然后对于每一个环就又回到了 \(k=1\) 的情况。

现在面临两个问题:第一,\(n\) 个数该如何分配到这些环中;第二,每个环中的数该如何排布。

对于问题一,我只知道结论:将 \(n\) 个数排序后,连续的一段分到一个环中(\(a_{1\sim n/\gcd(n,k)}\) 在一个环里,其他以此类推)。现在这个结论我只搜到了 EI 的证明,属实没看懂,太菜了,其他人的题解都省略了(摊手.jpg

对于问题二,继续将在环里的 \(p=n/\gcd(n,k)\) 个数排好序,那么环的排布一定是这样(假设 \(p\) 为奇数,偶数情况类似):

\[a_1,a_3,a_5,\dots,a_p,a_{p-2},a_{p-4},\dots,a_2
\]

证明:使用归纳法。对于 \(p=1,2,3\) 的情况显然成立,现假设对于 \(p=k\) 的情况成立,考虑 \(p=k+1\) 的情况。

现在 \(k\) 个数已排好,考虑将 \(a_{k+1}\) 插入其中。我们把将 \(a_{k+1}\) 插入 \(a_k,a_{k-1}\) 之间的情况与插入任意两个数 \(a_b,a_c\) 的情况做对比,得到

\[a_{k+1}\times a_k+a_{k+1}\times a_{k-1}-a_{k}\times a_{k-1}\ge a_{k+1}\times a_b+a_{k+1}\times a_c-a_b\times a_c
\]
\[a_{k+1}\times (a_k+a_{k-1}-a_b-a_c)\ge a_{k}\times a_{k-1}-a_b\times a_c
\]
\[{\rm LHS}\ge a_k^2+a_k\times a_{k-1}-a_k\times a_b-a_k\times a_c
\]

重新移项得

\[a_k^2+a_b\times a_c\ge a_{k}\times a_b+a_k\times a_c
\]

由排序不等式,证毕。

活整完了。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const int N=2e5+5;
  5. int n,m,a[N];
  6. ll f[N];
  7. int main()
  8. {
  9. scanf("%d%d",&n,&m);
  10. for(int i=1;i<=n;++i)
  11. scanf("%d",a+i),f[0]+=1LL*a[i]*a[i];
  12. sort(a+1,a+n+1);
  13. while(m--)
  14. {
  15. ll ans=0; int k; scanf("%d",&k);
  16. if(!k) {printf("%lld\n",f[k]); continue;}
  17. int p=n/__gcd(n,k);
  18. if(f[p]) {printf("%lld\n",f[p]); continue;}
  19. for(int i=1;i<=n;i+=p)
  20. {
  21. for(int j=0;j<p-2;++j)
  22. ans+=1LL*a[i+j]*a[i+j+2];
  23. ans+=1LL*a[i]*a[i+1]+1LL*a[i+p-1]*a[i+p-2];
  24. }
  25. printf("%lld\n",f[p]=ans);
  26. }
  27. return 0;
  28. }

NOI Online 提高组 题解的更多相关文章

  1. noip2010提高组题解

    NOIP2010提高组题解 T1:机器翻译 题目大意:顺序输入n个数,有一个队列容量为m,遇到未出现元素入队,求入队次数. AC做法:直接开1000的队列模拟过程. T2:乌龟棋 题目大意:有长度为n ...

  2. NOIP 2014 提高组 题解

    NOIP 2014 提高组 题解 No 1. 生活大爆炸版石头剪刀布 http://www.luogu.org/problem/show?pid=1328 这是道大水题,我都在想怎么会有人错了,没算法 ...

  3. NOIP 2001 提高组 题解

    NOIP 2001 提高组 题解 No 1. 一元三次方程求解 https://vijos.org/p/1116 看见有人认真推导了求解公式,然后猥琐暴力过的同学们在一边偷笑~~~ 数据小 暴力枚举即 ...

  4. NOIP 2000 提高组 题解

    NOIP2000 提高组 题解 No 1. 进制转换 https://www.rqnoj.cn/problem/295 水题 对于n和基数r, 每次用n mod r, 把余数按照逆序排列 注意 mod ...

  5. 【NOIP2018】提高组题解

    [NOIP2018]提高组题解 其实就是把写过的打个包而已 道路铺设 货币系统 赛道修建 旅行 咕咕咕 咕咕咕

  6. NOI On Line 提高组题解

    (话说其实我想填的是去年CSP的坑...但是貌似有一道题我还不会写咕咕咕... 先写一下这一次的题解吧. T1:序列.题意省略. 两种操作.这种题要先分析部分分 给出了全部都是2操作的子任务. 发现A ...

  7. noip2009提高组题解

    NOIP2009题解 T1:潜伏者 题目大意:给出一段密文和破译后的明文,一个字母对应一个密文字母,要求破译一段密文,如果有矛盾或有未出现密文无法破译输出failed,否则输出明文. 思路:纯模拟题 ...

  8. noip2008提高组题解

    第一题:笨小猴 模拟   第二题:火柴棒等式 搜索 深搜不用说,确定出两个加数然后判断能否拼出等式. 枚举确实不太好搞,因为枚举范围不确定,太大了容易超时,太小了容易漏解.不过这题的数据貌似很温和,我 ...

  9. noip2007提高组题解

    题外话:这一年的noip应该是最受大众关心的,以至于在百度上输入noip第三个关键字就是noip2007.主要是由于这篇文章:http://www.zhihu.com/question/2110727 ...

随机推荐

  1. 深入理解java虚拟机笔记Chapter3-内存分配策略

    内存分配策略 新生代和老年代的 GC 操作 新生代 GC 操作:Minor GC 发生的非常频繁,速度较块. 老年代 GC 操作:Full GC / Major GC 经常伴随着至少一次的 Minor ...

  2. go-zero:开箱即用的微服务框架

    go-zero 是一个集成了各种工程实践的 Web 和 rpc 框架,它的弹性设计保障了大并发服务端的稳定性,并且已经经过了充分的实战检验. go-zero 在设计时遵循了 "工具大于约定和 ...

  3. MySQL:聊一聊数据库中的那些锁

    在软件开发中,程序在高并发的情况下,为了保证一致性或者说安全性,我们通常都会通过加锁的方式来解决,在 MySQL 数据库中同样有这样的问题,一方面为了最大程度的利用数据库的并发访问,另一方面又需要保证 ...

  4. XML从入门到深入(超详细)

    一:什么是XML XML (eXtensible Markup Language)指可扩展标记语言,标准通用标记语言的子集,简称XML.是一种用于标记电子文件使其具有结构性的标记语言. XML可以标记 ...

  5. 一文说透 Go 语言 HTTP 标准库

    本篇文章来分析一下 Go 语言 HTTP 标准库是如何实现的. 转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com/archives/561 ...

  6. 如何在国产龙芯架构平台上运行c/c++、java、nodejs等编程语言

    高能预警:本文内容过于硬核,涉及编译器原理.cpu指令集.机器码.编程语言原理.跨平台原理等计算机专业基础知识,建议具有c.c++.java.nodejs等多种编程语言开发能力,且实战经验丰富的资深开 ...

  7. 3、mysql的多实例配置(2)

    4.设置mysql多实例启动脚本: (1)3306: [root@backup application]# cat /data/3306/mysql #!/bin/sh . /etc/init.d/f ...

  8. 详细解释 使用FileReference类加载和保存本地文件

    一般而言,用户不希望web浏览器中运行的应用程序访问电脑硬盘里的文件.然而,随着基于浏览器(browser-based)的富因特网应用程序的增多,一些应用程序迫切需要访问用户所选择的文件,或者将文件保 ...

  9. 关于 Index '8' specified is out of bounds.

    报类似这样的错误暂时我只发现了两个原因: 1, 数组超出了界线,这个自己多多注意,加判断,在循环的时候看看是不是有结束条件 2, 你需要提交的网页不存在.有可能是因为你没有这个文件.可能是你的文件名错 ...

  10. Spring:Spring-IOC三种注入方式、注入不同数据类型

    一.Spring IOC(依赖注入的三种方式): 1.Setter方法注入 package com.jpeony.spring.setter; import com.jpeony.spring.com ...