来补坑了……

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

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


T1 序列

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

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

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

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

#include <bits/stdc++.h>
using namespace std;
typedef long long ll; const int N=1e5+7;
int n,m,cnt,a[N],b[N],p[N],q[N],fa[N];
bool vis[N];
ll s[N],c[3];
vector<int> G[N]; int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);} bool dfs(int u,int col)
{
vis[u]=col,c[col]+=s[u];
bool fl=1;
for(int i=0,v;i<G[u].size();++i)
{
if(c[u]==c[v=G[u][i]]) fl=0;
if(!vis[v]&&!dfs(v,3-col)) fl=0;
}
return fl;
} int main()
{
int T; scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m); cnt=0;
for(int i=1;i<=n;++i) fa[i]=i,G[i].clear(),scanf("%d",a+i);
for(int i=1;i<=n;++i) s[i]=vis[i]=0,scanf("%d",b+i);
for(int i=1,op,x,y;i<=m;++i)
{
scanf("%d%d%d",&op,&x,&y);
if(op==2) fa[find(x)]=find(y);
else p[++cnt]=x,q[cnt]=y;
}
for(int i=1;i<=n;++i) s[find(i)]+=b[i]-a[i];
for(int i=1;i<=cnt;++i)
{
int u=find(p[i]),v=find(q[i]);
G[u].push_back(v);
G[v].push_back(u);
}
bool ok=1;
for(int i=1;i<=n;++i)
if(find(i)==i&&!vis[i])
{
c[1]=c[2]=0;
bool fl=dfs(i,1);
if(fl&&c[1]!=c[2]) {ok=0; break;}
if(!fl&&((c[1]+c[2])&1)) {ok=0; break;}
}
puts(ok?"YES":"NO");
}
return 0;
}

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)\)。

#include <bits/stdc++.h>
#define lb(x) (x&(-x))
using namespace std;
typedef long long ll; const int N=2e5+5;
int n,m,a[N],b[N],cnt[N];
ll tot,c[N]; void add(int x,ll k) {for(;x<=n;x+=lb(x)) c[x]+=k;}
ll ask(int x) {ll res=0; for(;x;x-=lb(x)) res+=c[x]; return res;} int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%d",a+i);
b[i]=i-1-ask(a[i]);
tot+=b[i],++cnt[b[i]];
add(a[i],1);
}
memset(c,0,sizeof(c));
add(1,tot); tot=0;
for(int i=1;i<=n;++i)
{
tot+=cnt[i-1];
add(i+1,-(n-tot));
}
while(m--)
{
int op,x; scanf("%d%d",&op,&x);
x=min(x,n-1);
if(op==1)
{
swap(a[x],a[x+1]);
swap(b[x],b[x+1]);
if(a[x]<a[x+1])
{
--b[x];
add(1,-1);
add(b[x]+2,1);
}
else
{
++b[x+1];
add(1,1);
add(b[x+1]+1,-1);
}
}
else printf("%lld\n",ask(x+1));
}
return 0;
}

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
\]

由排序不等式,证毕。

活整完了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll; const int N=2e5+5;
int n,m,a[N];
ll f[N]; int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
scanf("%d",a+i),f[0]+=1LL*a[i]*a[i];
sort(a+1,a+n+1);
while(m--)
{
ll ans=0; int k; scanf("%d",&k);
if(!k) {printf("%lld\n",f[k]); continue;}
int p=n/__gcd(n,k);
if(f[p]) {printf("%lld\n",f[p]); continue;}
for(int i=1;i<=n;i+=p)
{
for(int j=0;j<p-2;++j)
ans+=1LL*a[i+j]*a[i+j+2];
ans+=1LL*a[i]*a[i+1]+1LL*a[i+p-1]*a[i+p-2];
}
printf("%lld\n",f[p]=ans);
}
return 0;
}

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后端知识点梳理——JVM

    可以先看看我的深入理解java虚拟机笔记 深入理解java虚拟机笔记Chapter2 深入理解java虚拟机笔记Chapter3-垃圾收集器 深入理解java虚拟机笔记Chapter3-内存分配策略 ...

  2. 再看 Java 中的单例

    此前面试遇到了单例问题,本以为已经背的滚瓜烂熟,没想到被问单例如何避免被反射和序列化破坏,虽然后来还是等到了通知,但还是复习一下单例的实现方式,并学习防止反射和序列化破坏的手段. 基本实现方式 其他相 ...

  3. 08:jQuery(01)

    今日内容概要 jQuery(封装了js的前端框架(模块)) 很容易与DOM操作混淆 jQuery """ jQuery内部封装了原生的js代码(还额外添加了很多的功能) ...

  4. nacos 实战(史上最全)

    文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...

  5. VBS脚本编程(5)——过程与函数

    过程是构成程序的一个模块,往往用来完成一个相对独立的功能.过程可以使程序更清. Sub过程与Function函数的区别: Sub没有返回值,Function有返回值: Sub不能放在表达式中,Func ...

  6. 学习JDK源码(一):String

    用了好久的Java了,从来没有看过jdk的源码,趁着今天有点时间,拿出了jdk的源码看了下,今天先看了关于String的,毕竟开发中String类型使用最广泛.在我们下载安装jdk的时候,部分源码也已 ...

  7. Java语言实现二维码的生成

    众所周知,现在生活中二维码已经是无处不见.走在街道上,随处可见广告标语旁有二维码,手机上QQ,微信加个好友都能通过二维码的方式,我不知道是什么时候兴起的二维码浪潮,但是我知道,这在我小时候可是见不到的 ...

  8. C++知识点大汇总

    概述 1.1980年 贝尔实验室 Bjanre Stroustrup(比雅尼·斯特劳斯特鲁普)对C改进与扩充 最初称为"带类的C",(c with classes). 1983年正 ...

  9. 一文读懂高速PCB设计跟高频放大电路应用当中的阻抗匹配原理

    这一期课程当中,我们会重点介绍高频信号传输当中的阻抗匹配原理以及共基极放大电路在高频应用当中需要注意的问题,你将会初步了解频率与波长的基础知识.信号反射的基本原理.特性阻抗的基本概念以及怎么样为放大电 ...

  10. sql数据库新建作业,新建步骤时报错从 IClassFactory 为 CLSID 为 {AA40D1D6-CAEF-4A56-B9BB-D0D3DC976BA2} 的 COM 组件创建实例失败,原因是出现以下错误: c001f011。 (Microsoft.SqlServer.ManagedDTS)

    简单粗暴的重启sql数据库 其他网上找的方法 32位操作系统: 打开运行(命令提示符), 一.输入 cd c:\windows\system32 进入到c:\windows\system32路径中 二 ...