来补坑了……

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

还有要说一嘴,为啥我在其他网站代码都好好的,复制到 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. 如果在num1的任何位置有一个数字的连续三倍,并且在num2中有一个数字的连续两倍,则返回1。 如果不是这样,则返回0

    ''' 它接受数字num1和num2,如果在num1的任何位置有一个数字的连续三倍,并且在num2中有一个数字的连续两倍,则返回1. 如果不是这样,则返回0 例子 triple_double(4519 ...

  2. 【NX二次开发】Block UI 反向

    属性说明 属性   类型   描述   常规           BlockID    String    控件ID    Enable    Logical    是否可操作    Group    ...

  3. NOIP模拟测试16「Drink·blue·weed」

    话说这次考试 Drink 非常棒的一道卡常练习题,适合练习卡常 真的很棒 前置卡常知识 1.char要比int快 char是最快的 输出putchar,输入getchar 在这个题快了7000豪 2. ...

  4. 携程二面:讲讲 MySQL 中的 WAL 策略和 CheckPoint 技术

    前段时间我在准备暑期实习嘛,这是当时面携程的时候二面的一道问题,我一脸懵逼,赶紧道歉,不好意思不知道没了解过,面试官又解释说 redo log,我寻思着 redo log 我知道啊,WAL 是啥?给面 ...

  5. Linux 安装 git

    安装方法参考:http://www.jb51.net/os/RedHat/149653.html 具体内容: 在安装Git之前,需要先安装一些依赖包,安装依赖包之前可以先检查下是否已经安装. shel ...

  6. vue v-if条件判断

    <view class='circle-G' v-if="item.status === 'G'"></view> <view class='circ ...

  7. layui 修改表格边框颜色

    /*设置不可编辑时字体颜色*/ .layui-disabled, .layui-disabled:hover { color: #101010 !important; background-color ...

  8. 同步工具——Exchanger

    本博客系列是学习并发编程过程中的记录总结.由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅. 并发编程系列博客传送门 本文是转载文章,原文请见这里 一.Exchanger简介 ...

  9. 选择适合小企业的CRM软件

    随着信息时代的到来和客户掌握的信息变多,大多数企业开始从"以产品为中心"转变为"以客户为中心".为了适应市场的变化,许多企业开始使用客户关系管理软件来提高工作效 ...

  10. mqtt 集成

    -- 在pom.xml导入依赖 <!-- mqtt --> <dependency> <groupId>org.springframework.boot</g ...