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. golang切片的一些自问自答

    你好,我是轩脉刃.这篇是关于go切片的一些问题和回答. go的切片基本上是代码中使用最多的一种数据结构了,使用这种数据结构有哪些要注意的点,这个是非常必要了解的东西.基本上,以前写的一篇博客 http ...

  2. 【Java笔记】Java分包问题

    这个图讲的很清晰,转自-http://www.bubuko.com/infodetail-2219664.html

  3. Saliency maps

    目录 问题 细节 变量 扩展 代码 Deep Inside Convolutional Networks: Visualising Image Classification Models and Sal ...

  4. 深入 Laravel 内核之 PHP 反射机制和依赖注入

    结论: PHP中提供了反射类来解析类的结构: 通过反射类可以获取到类的构造函数及其参数和依赖: 给构造函数的参数递归设置默认值后,即可使用这些带默认值的参数通过 newInstanceArgs 实例化 ...

  5. HTTP 状态码整理

  6. java POJO中 Integer 和 int 的不同,用int还是用Integer

    https://www.jianshu.com/p/ff535284916f [int和Integer的区别] int是java提供的8种原始类型之一,java为每个原始类型提供了封装类,Intege ...

  7. spring boot + thymeleaf +security自定义规则 的简单使用

    1.前言 以前开发一直使用 springMVC模式开发 ,前端页面常使用 JSP  ,现在html5淘汰了 ,要么使用html ,要么使用vue , 现在使用spring boot ,有必要总结一下 ...

  8. PowerPoint2010实现折线图动态展示

    原文链接:https://www.toutiao.com/i6797629648881582596/ 我们经常会制作折线图表来表达一个过程的趋势变化,而如果让折线图动起来,会更加的生动.接下来我们将一 ...

  9. Yum安装Maven

    一.安装 wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum. ...

  10. 服务性能监控之Micrometer详解

    Micrometer 为基于 JVM 的应用程序的性能监测数据收集提供了一个通用的 API,支持多种度量指标类型,这些指标可以用于观察.警报以及对应用程序当前状态做出响应. 通过添加如下依赖可以将 M ...