A

首先从 \(t = 2\) 的特殊部分分出发。

不难发现这个操作是很不直观的,于是可以考虑对于每个操作 \((u, v)\) 在 \(u, v\) 之间连一条无向边。

显然连通块之间要分开考虑,对于同一个联通块,一次操作连通块内权值之和没有改变。

于是可以令 \(c_i = a_i - b_i\),那么 \(a\) 序列变到 \(b\) 序列等价于 \(c\) 序列变为全 \(0\)。

由此可见一个连通块内全变为 \(0\) 的必要条件是连通块内权值总和为 \(0\),继续观察发现这是充要条件。简单证明如下:

从最简单特殊的连通图-树出发,可以发现叶子节点若要消去无论如何需要恰好进行确定的一些操作。

那么可以先将子树内全部清 \(0\),最后就只剩下了根节点。因为权值总和为 \(0\),因此根节点此时权值必为 \(0\)。

对于一般的联通图,任意选择一颗 \(dfs\) 树即可。

这启示我们在解决联通图的判定性问题时,往往可以从最简单的树出发,然后考虑简单环,然后基环树...

于是可以直接使用并查集维护这个过程,复杂度 \(\mathcal{O(Tn \log n)}\)。

回到原问题,不难发现通过上述的做法(证明)可以发现:用操作二连边起来的同意连通块内权值可以互相转移,于是可以考虑先将这个联通块缩起来方便下面的考虑。

之后我们同样在缩起来的连通块之间用操作一连边。

同样从最简单特殊的树出发,还是从叶子节点开始考虑。

那么每个点最终对根节点的贡献均为 \((-1) ^ {dep} \times c_i\),若最终根节点为 \(0\) 即可满足条件。

若可以换根,则充要条件应为:任选一个根向下黑白染色,黑色点权和与白色点权和相等。

这启示着我们要往二分图的方面思考,不难证明:二分图合法当且仅当两部权值之和相等。

那么对于一个非二分图,一定可以将所有点权运送到奇环上然后消去,但前提是点权之和必须为偶数。

总结一下本题做法:

  1. 首先按照操作二连边缩点后再按照操作一连边。

  2. 单独考虑每个连通块。若为二分图,判定条件为:两部权值相等;否则判定条件为:点权之和为偶数。

复杂度 \(\mathcal{O(Tn \log n)}\)。

#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define Next(i, u) for (int i = h[u]; i; i = e[i].next)
const int N = 1e5 + 5;
struct edge { int v, next;} e[N << 1];
struct node { int u, v;} r[N];
int T, n, m, u, v, F, ans, opt, tot, cnt, w[3], a[N], b[N], h[N], co[N];
int read() {
char c; int x = 0, f = 1;
c = getchar();
while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
namespace dsu {
int w[N], fa[N], sz[N];
void reset() { rep(i, 1, n) fa[i] = i, w[i] = a[i] - b[i], sz[i] = 1;}
int find(int x) { return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}
void Merge(int x, int y) {
int a = find(x), b = find(y);
if(a == b) return ;
if(sz[a] > sz[b]) swap(a, b);
fa[a] = b, w[b] += w[a], sz[b] += sz[a];
}
}
void add(int u, int v) {
e[++tot].v = v, e[tot].next = h[u], h[u] = tot;
e[++tot].v = u, e[tot].next = h[v], h[v] = tot;
}
void dfs(int u, int col) {
w[col] += dsu :: w[u], co[u] = col;
Next(i, u) {
int v = e[i].v;
if(!co[v]) dfs(v, 3 - col);
else if(co[v] != 3 - col) F = true;
}
}
int main () {
T = read();
while (T--) {
n = read(), m = read();
rep(i, 1, n) a[i] = read();
rep(i, 1, n) b[i] = read();
cnt = 0, ans = 1, dsu :: reset();
rep(i, 1, m) {
opt = read(), u = read(), v = read();
if(opt == 1) r[++cnt] = (node){u, v};
else dsu :: Merge(u, v);
}
tot = 0, memset(h, 0, sizeof(h));
memset(co, 0, sizeof(co));
rep(i, 1, cnt) add(dsu :: find(r[i].u), dsu :: find(r[i].v));
rep(i, 1, n) if(dsu :: find(i) == i && !co[i]) {
F = w[1] = w[2] = 0, dfs(i, 1);
if(!F) ans &= (w[1] == w[2]);
else ans &= !((w[1] + w[2]) & 1);
}
printf(ans ? "YES\n" : "NO\n");
}
return 0;
}

B

可以发现,冒泡排序的本质是第 \(i\) 轮时将 \(n - i + 1\) 向后挪直至排好。

因此每一轮冒泡排序后考虑以 \(i\) 为结尾的逆序对个数 \(f_i\),相当于将 \(f\) 向左移一位后将非 \(0\) 的位置减 \(1\)。

因此 \(k\) 轮冒泡排序后逆序对的数量本质上是 \(\sum\limits_{i > k} f_i(f_i > k) - k \times \sum\limits_{i > k} [f_i > k]\)。

注意到一定有 \(f_i < k(i \le k)\),所以等价于求 \(\sum f_i(f_i > k) - k \times \sum [f_i > k]\)。

使用权值树状数组维护即可,修改只需要单点修改。

复杂度 \(\mathcal{O((n + m) \log n)}\)。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; ++i)
const int N = 2e5 + 5;
int n, m, x, opt, p[N], a[N];
struct FT {
#define lowbit(x) (x & (-x))
int c[N];
void add(int p, int k) { if(!p) return ; for (int i = p; i <= n; i += lowbit(i)) c[i] += k;}
int query(int p) { int ans = 0; for (int i = p; i; i -= lowbit(i)) ans += c[i]; return ans;}
}T[3];
int read() {
char c; int x = 0, f = 1;
c = getchar();
while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
signed main () {
n = read(), m = read();
rep(i, 1, n) {
p[i] = read(), a[i] = T[0].query(n) - T[0].query(p[i]);
T[0].add(p[i], 1), T[1].add(a[i], 1), T[2].add(a[i], a[i]);
}
while (m--) {
opt = read(), x = read();
if(opt == 1) {
T[1].add(a[x], -1), T[1].add(a[x + 1], -1);
T[2].add(a[x], -a[x]), T[2].add(a[x + 1], -a[x + 1]);
if(p[x] > p[x + 1]) --a[x + 1];
else ++a[x];
swap(p[x], p[x + 1]), swap(a[x], a[x + 1]);
T[1].add(a[x], 1), T[1].add(a[x + 1], 1);
T[2].add(a[x], a[x]), T[2].add(a[x + 1], a[x + 1]);
}
else {
if(x >= n) { puts("0"); continue ;}
int cnt = T[1].query(n) - T[1].query(x);
printf("%lld\n", T[2].query(n) - T[2].query(x) - cnt * x);
}
}
return 0;
}

C

首先考虑一下 \(k = 1\) 的特殊情况,可以猜想尽可能让大的数字相乘是更优的。

下面的证明是学的 EI 的方法:

单纯从代数的角度发现没有出路,于是可以考虑将这个东西数形结合,直观地表示出来。

通过乘积可以联想到面积,但两个数的乘积不好直接表示但可知:\(\frac{a_i ^ 2 + a_j ^ 2 - (a_i - a_j) ^ 2}{2} = a_i \times a_j\) 于是原问题等价于求 \(\sum a_i ^ 2 - \frac{\sum\limits (a_i - a_{i + 1}) ^ 2}{2}\) 的最大值,前者是一个定值,只需最小化后者即可。

不难发现,后者可以被看作是 \((a_i, a_i) \sim (a_{i + 1}, a_{i + 1})\) 组成的正方形。

于是问题进一步转化为从环上 \(p_1\) 开始走完一圈,每次加入 \((a_{p_i}, a_{p_i}) \sim (a_{p_{i + 1}}, a_{p_{i + 1}})\) 构成正方形的面积,最小化所有正方形面积之和。

下面我们定义 \(a - b\) 为从 \(a\) 中去除 \(b\) 的部分,\((a_i, a_j)\) 表示 \((a_i, a_i) \sim (a_j, a_j)\) 构成的正方形。

仔细分析可以发现因为经过某个点一次之后必定回来,所以 \(\forall i, (a_i, a_{i + 1})\) 至少要被算入两次。

同时,因为点不能重复到达,所以若存在某一次经过了 \((a_i, a_{i + 1})\) 则必定会经过 \((a_i, a_{i + 2})\),因此 \((a_i, a_{i + 2}) - (a_i, a_{i + 1}) - (a_{i + 1}, a_{i + 2})\) 至少会被算入一次。

此时可以发现,上面的贪心恰好达到了上述分析的下界。

那么对于 \(k > 1\) 的情况,仔细分析会发现若从 \(1\) 出发后经过一定的此时会回到 \(1\) 开始循环,这样的次数是满足 \(kx \equiv 0\pmod{n}\) 的最小整数解 \(x\),不难发现为 \(\frac{n}{\gcd(n, k)}\),于是这张图等价于被分成了 \(\gcd(n, k)\) 个大小为 \(\frac{n}{\gcd(n, k)}\) 环,因此本质不同的图只有 \(d(n)\) 个。

于是现在的问题就转化为合理分配权值给 \(x(x \mid n)\) 个长度为 \(\frac{n}{x}\) 的环,使得每个环内部进行 \(k = 1\) 的贪心后总权值最大。

基于观察上面贪心证明的观察可以发现:

  1. 不论如何分配,\(\sum a_i ^ 2\) 始终是一个定值,于是只需最小化每个环内 \(\frac{\sum\limits (a_i - a_{i + 1}) ^ 2}{2}\) 的值。

  2. 基于 \(1\) 及上述贪心证明可知,删除一个环中的最大值或最小值一定更优。

  3. 基于 \(1\) 及上述贪心证明可知,将一个一个不大于最大值或不小于最小值的值加入一定会使得答案更优。

因此我们可以发现,两个环的分配值域一定不会相交,否则可以交换端点(满足上述 \(2, 3\) 性质)使得答案更优。

总结一下本题做法:

  1. 本质不同的方案只有 \(d(n)\) 种,\(\forall k\) 会将环分成 \(\gcd(n, k)\) 个大小为 \(\frac{n}{\gcd(n, k)}\) 的 \(k = 1\) 的特殊情形。

  2. 最优的分配权值进入每个环的方案一定是 \(a_1 \sim a_{\frac{n}{\gcd(n, k)}}, a_{\frac{n}{\gcd(n, k)} + 1} \sim a_{\frac{n}{\gcd(n, k)} \times 2} \cdots\) 依次分配进每个环。

  3. 对于每个环内 \(k = 1\) 的特殊情况,一定是按照 \(a_1 \cdots a_{n - 1}, a_n, a_{n - 2} \cdots a_2(a_1 \le a_2 \le \cdots \le a_n)\) 的方式放置最优。

接下来对于每个 \(d \mid n\) 模拟上述流程即可,复杂度 \(\mathcal{O(nd(n))}\)。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; ++i)
const int N = 2e5 + 5;
int n, m, k, a[N], ans[N];
int read() {
char c; int x = 0, f = 1;
c = getchar();
while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int Gcd(int a, int b) { return !b ? a : Gcd(b, a % b);}
int solve(int k) {
int len = n / k, ans = 0;
rep(i, 1, k) {
ans += a[(i - 1) * len + 1] * a[(i - 1) * len + 2];
ans += a[i * len] * a[i * len - 1];
rep(j, 1, len - 2) ans += a[(i - 1) * len + j] * a[(i - 1) * len + j + 2];
}
return ans;
}
signed main () {
n = read(), m = read();
rep(i, 1, n) a[i] = read(), ans[0] += a[i] * a[i];
sort(a + 1, a + n + 1);
rep(i, 1, n / 2) if(n % i == 0) ans[i] = solve(i);
rep(i, 1, m) {
k = read();
printf("%lld\n", !k ? ans[k] : ans[Gcd(k, n)]);
}
return 0;
}

[NOI Online #1 提高组]的更多相关文章

  1. luogu P6570 [NOI Online #3 提高组]优秀子序列 二进制 dp

    LINK:P6570 [NOI Online #3 提高组]优秀子序列 Online 2的T3 容易很多 不过出于某种原因(时间不太够 浪了 导致我连暴力的正解都没写. 容易想到 f[i][j]表示前 ...

  2. [NOI Online 2021 提高组] 积木小赛

    思路不说了. 想起来自己打比赛的时候,没睡好.随便写了个\(HASH\),模数开小一半分都没有. 然后学了\(SAM\),发现这个判重不就是个水题. \(SAM\)是字串tire的集合体. 随便\(d ...

  3. [NOI Online #2 提高组]涂色游戏 题解

    题目描述 你有 1020 个格子,它们从 0 开始编号,初始时所有格子都还未染色,现在你按如下规则对它们染色: 编号是 p1 倍数的格子(包括 0号格子,下同)染成红色. 编号是 p2 倍数的格子染成 ...

  4. NOI Online #2 提高组 游戏

    没用二项式反演的菜比. 题目链接 Solution 非平局代表的树上祖先关系是比较好统计,(可以在处理一个点时,考虑用他去匹配他的子树中的东西)而平局的关系比较难统计.我们不妨求出至少 \(k\) 个 ...

  5. NOI Online #2 提高组 游记

    没 NOI Online 1 挂的惨就来写游记吧,不知道为啥 NOI Online 1 民间数据测得 60 分的 T1 最后爆零了... 昏昏沉沉的醒来,吃了早饭,等到 \(8:30\) 进入比赛网页 ...

  6. NOI Online #3 提高组 T1水壶 题解

    题目描述 有 n 个容量无穷大的水壶,它们从 1∼n 编号,初始时 i 号水壶中装有 Ai 单位的水. 你可以进行不超过 k 次操作,每次操作需要选择一个满足 1≤x≤n−1 的编号 x,然后把 x ...

  7. NOI On Line 提高组题解

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

  8. NOI Online #3 提高组 游记

    考的好就来写游记吧 2020.5.24 星期日 上一天晚上为了班里事物做 PPT 肝到 11:30,这比赛就打打玩玩.第二天醒来有点昏昏沉沉的感觉. 打开题面,一看 T1,好像是个性质极其简单的前缀和 ...

  9. [NOI Online #3 提高组] 魔法值

    现在只会\(O(qn^3log)\)的\(40pts\)做法,鸽了. 反正就是预处理之后,去掉一个\(n\). 我预处理了,没去\(n\),hhhh,成功减少了一半的常数.

  10. 洛谷 P6570 - [NOI Online #3 提高组] 优秀子序列(集合幂级数+多项式)

    洛谷题面传送门 首先 \(3^n\) 的做法就不多说了,相信对于会状压 dp+会枚举子集的同学来说不算困难(暴论),因此这篇博客将着重讲解 \(2^nn^2\) 的做法. 首先如果我们把每个 \(a_ ...

随机推荐

  1. CS5216 设计于DP转HDMI转换器|DP转HDMI 1080P中继器 电平转化器开关设计方案与线路图

    CS5216是一款Displayport to hdmi 1080p音视频信号转换芯片,主要用于设计与开发DP转HDMI 转换器.中继器.电平转换器等产品当中.它支持交流和直流耦合TMDS信号高达1. ...

  2. jboss CVE-2015-7501 反序列化漏洞复现

    JBOSS反序列化漏洞 环境: vulfocus jboss CVE-2015-7501 云服务器 kali攻击机 基本原理:JBoss在/invoker/JMXInvokerServlet请求中读取 ...

  3. SpringCloud发现服务代码(EurekaClient,DiscoveryClient)

    1.说明 本文介绍SpringCloud发现服务代码的开发, 通过使用EurekaClient,DiscoveryClient来发现注册中心的服务等, 从而可以自定义客户端对注册中心的高级用法. 2. ...

  4. Kerberos加密级别不支持的问题

    1.问题现象 Zookeeper开启kerberos认证后出现如下问题,不支持的加密级别: 2017-02-23 09:20:57,048 [myid:1] - WARN [NIOWorkerThre ...

  5. CSS基础 装饰 元素本身隐藏和显示效果及案例

    1.visibility:hidden; 2.display: none: 区别: 1.visibility:hidden 隐藏元素本身,且在网页中 占位置 2.display:none; 隐藏元素本 ...

  6. CSS基础 overflow 内容溢出部分显示效果

    属性:overflow 值 作用 visible 默认,内容溢出部分可见 hidden 内容溢出部分不可见 scroll 内容有无溢出,都有滚动条 auto 有内容溢出,自动显示滚动条

  7. 年功序列c++游戏

    题目描述 在虚拟国度里多了很多 Virtual oier,为了树立对后辈的威信,从第 11 个 Virtual oier 开始的 oier 们搞起了年功序列的制度. 虚拟国度的创始人 oier Cht ...

  8. hadoop 之 某一个datanode启动失败(Initialization failed for Block pool <registering> (Datanode Uuid unassigned) service to)

    环境 集群7台 master 3台 datanode 4台 每个datanode有12个硬盘 场景 启动集群之后,发现有一台datanode未启动,手动启动,还是未启动.查看日志,发现: Initia ...

  9. 初识python: flush 实现进度条打印

    通过flush(强制刷新)实现,类似进度条打印: #!/user/bin env python # author:Simple-Sir # time:20180918 #打印进度条 import sy ...

  10. Elasticsearch打造全文搜索引擎(一)

     带着问题上路--ES是如何产生的? (1)思考:大规模数据如何检索? 如:当系统数据量上了10亿.100亿条的时候,我们在做系统架构的时候通常会从以下角度去考虑问题: 1)用什么数据库好?(mysq ...