2020.8.27 解题报告
2020.8.27
答题情况
总成绩 : 50 , 排名 : 4 / 6
T1 : 20 T2 : 0 T3 : 30
各题目分析
题目 1 :
预估成绩 : 100 实际成绩 : 20 考试用时 : 7:30 ~ 8:10
先想到爆搜,觉得过不去写了贪心。
贪心被卡了。
题目 2 :
预估成绩 : 0 实际成绩 : 0 考试用时 : 10:00 ~ 10:40
思维题,想不到。
题目 3 :
预估成绩 : 40 实际成绩 : 30 考试用时 : 8:20 ~ 9:50
写了 \(O(n^2\log n)\) 的暴力。
题目解析
T1
\(n\le 10^9\),最多只有 9 位,\(t<9\),任意次交换后至多有 \(9!=362880\) 种结果。
考虑爆搜枚举交换的位置,交换次数不多于 \(\min(k, t!)\)。
搜出最大/最小值即可。
T2
考虑两次连续的操作 \([l,l+k-1]\) 与 \([l+1,l+k]\),仅有 \(l\) 和 \(l+k\) 状态被改变。
同理,考虑四次连续的操作 \([l,l+k-1], [l+1,l+k], [l+k,l+2k-1]\) 和 \([l+k+1, l+2k]\),仅有 \(l\) 和 \(l+2k\) 状态被改变。
发现可令任意两个距离为 \(k\) 的倍数的位置同时取反。
考虑每 \(k\) 个位置分为一组,每一组相同位置的数 距离为 \(k\) 的倍数。
考虑对相同位置的数进行操作。
若这些数中有偶数个 \(0\),则可以通过每次减少两个 \(0\),使它们全部变为 \(1\),从而对答案有贡献。
如果有奇数个 \(1\),进行若干次操作后,必然会剩下一个 \(0\) 位置。可通过多次操作使 \(0\) 位置出现在任何一个数的地方,应使 \(0\) 位置位于权值较小的数上。
仅仅这样做似乎有些漏洞,比如这组数据:\(\text{1001}\)。
如果仅按照上面的做法,那么钦定了 \(1{\underline {0}}0{\underline {1}}\) 这两个位置 \(01\) 情况必须不同。
考虑先进行一次操作,变为:\(\text{0101}\),改变了相同位置的数的奇偶性,不会漏解。
T3
考虑一条边 \((x,y, w)\) 的影响。
发现他只影响其祖先节点的答案。
设 \(x\) 为树上父节点,则 \((x,y, w)\) 对 \(ans_x\) 的影响为 \(w\times size_y\times (size_x - size_y)\)。
祖先节点同理,设 \(z\) 为祖先,则 \((x,y, w)\) 对 \(ans_z\) 的贡献为 \(w\times size_y \times (size_z - size_y)\)。
贡献仅与 \(size_y\) 有关,考虑将边权转为点权,赋值给 \(y\)。
考虑树上一个节点 \(x\) 的答案,化下式子:
\(\sum\) 项仅与 \(y\) 有关,考虑线段树维护这两个值的区间和,上树剖即可。
复杂度 \(O(m\log^2 n)\)。
代码实现
T1 :
考场代码
//
/*
By:Luckyblock
*/
#include <algorithm>
#include <cstdio>
#include <ctype.h>
#include <cstring>
#define ll long long
const int kMaxn = 10 + 10;
//=============================================================
int t, num[kMaxn], sorted[kMaxn], rk[kMaxn];
int tmpnum[kMaxn], tmprk[kMaxn];
//=============================================================
inline ll read() {
ll f = 1, w = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
ll GetMax(ll k_) {
for (int i = 1; i <= t; ++ i) {
tmpnum[i] = num[i];
tmprk[i] = rk[i];
}
for (int i = 1; i <= t && k_; ++ i) {
int rki = tmprk[i], rkj = 0, pos;
for (int j = i + 1; j <= t; ++ j) {
if (tmprk[j] > rkj) {
rkj = tmprk[j];
pos = j;
}
}
if (rkj > rki) {
std :: swap(tmpnum[i], tmpnum[pos]);
std :: swap(tmprk[i], tmprk[pos]);
-- k_;
}
}
ll ret = 0, pow = 1;
for (int i = t; i >= 1; -- i) {
ret += tmpnum[i] * pow;
pow *= 10;
}
return ret;
}
ll GetMin(ll k_) {
for (int i = 1; i <= t; ++ i) {
tmpnum[i] = num[i];
tmprk[i] = rk[i];
}
for (int i = 1; i <= t && k_; ++ i) {
int rki = tmprk[i], rkj = 10, pos;
for (int j = i + 1; j <= t; ++ j) {
if (i == 1) {
if (tmprk[j] < rkj && tmpnum[j]) {
rkj = rk[j];
pos = j;
}
continue ;
}
if (tmprk[j] < rkj) {
rkj = tmprk[j];
pos = j;
}
}
if (rkj < rki) {
std :: swap(tmpnum[i], tmpnum[pos]);
std :: swap(tmprk[i], tmprk[pos]);
-- k_;
}
}
ll ret = 0, pow = 1;
for (int i = t; i >= 1; -- i) {
ret += tmpnum[i] * pow;
pow *= 10;
}
return ret;
}
//=============================================================
int main() {
freopen("cooperate.in", "r", stdin);
freopen("cooperate.out", "w", stdout);
ll T = read();
while (T --) {
ll n = read(), k = read(), tmp = n;
for (t = 0; tmp; tmp /= 10) {
++ t;
num[t] = sorted[t] = tmp % 10;
}
std :: sort(sorted + 1, sorted + t + 1);
for (int i = 1, j = t; i <= j; ++ i, -- j) std :: swap (num[i], num[j]);
for (int i = 1; i <= t; ++ i) {
for (int j = 1, rkj = 0; j <= t; ++ j) {
if (sorted[j] != sorted[j - 1]) rkj ++;
if (sorted[j] == num[i]) {
rk[i] = rkj;
break;
}
}
}
printf("%lld\n", GetMax(k) - GetMin(k));
}
return 0;
}
正解
#include<bits/stdc++.h>
using namespace std;
int maxx, minn, k, len;
int c[20], sum1[20], sum2[20], p[20];
char ss[20];
void update() {
if (c[p[1]] == 0) return;
for (int i = 1; i <= len; i++)
sum1[i] = p[i];
int kk = 0, s = 0;
for (int i = 1; i <= len; i++) {
s = s * 10 + c[p[i]];
if (sum1[i] != i) {
for (int j = i+1; j <= len; j++) {
if (sum1[j] == i) {
swap(sum1[i], sum1[j]);
kk++;
if(kk > k) return;
break;
}
}
}
}
if(kk > k) return;
maxx = max(maxx, s);
minn = min(minn, s);
}
int main() {
freopen("cooperate.in", "r", stdin);
freopen("cooperate.out", "w", stdout);
int t;
scanf("%d", &t);
while(t--) {
memset(sum1,0,sizeof(sum1));
memset(sum2,0,sizeof(sum2));
scanf("%s%d",ss,&k);
len = strlen(ss);
for(int i = 0; i < len; i++) {
c[i+1] = ss[i] - '0';
sum1[c[i+1]]++;
sum2[c[i+1]]++;
}
if(k >= len-1) {
minn = maxx = 0;
for(int i = 1; i <= 9; i++) {
if(sum1[i]) {
minn = minn*10 + i;
sum1[i]--;
break;
}
}
for(int i = 0; i <= 9; i++) {
while(sum1[i]) {
minn = minn*10 + i;
sum1[i]--;
}
}
for(int i = 9; i >= 0; i--) {
while(sum2[i]) {
maxx = maxx*10 + i;
sum2[i]--;
}
}
printf("%d\n", maxx - minn);
continue;
}
for(int i = 1; i <= len; i++)
p[i] = i;
minn = 2e9, maxx = -1;
do
update();
while(next_permutation(p+1, p+len+1));
printf("%d\n", maxx - minn);
}
return 0;
}
T2:
正解
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstdlib>
using namespace std;
#define maxN 2050
#define oo 10000000
int T;
int N, K;
int state[maxN], a[maxN];
int Solve(int N, int K, int state[], int a[])
{
int res = 0;
for (int i = 1; i <= K; ++ i)
{
int cnt = 0, minv = oo;
for (int j = i; j <= N; j += K)
{
res += a[j];
minv = min(minv, a[j]);
if (state[j] == 0) ++ cnt;
}
if (cnt & 1) res -= minv;
}
return res;
}
int main()
{
freopen("challenge.in", "r", stdin);
freopen("challenge.out", "w", stdout);
scanf("%d", &T);
while (T--)
{
scanf("%d%d", &N, &K);
for (int i = 1; i <= N; ++ i) scanf("%d", state+i);
for (int i = 1; i <= N; ++ i) scanf("%d", a+i);
int Ans = 0;
if (K == 1)
{
for (int i = 1; i <= N; ++ i) Ans += a[i];
}
else
{
Ans = Solve(N, K, state, a);
for (int i = 1; i <= K; ++ i) state[i] ^= 1;
Ans = max(Ans, Solve(N, K, state, a));
}
printf("%d\n", Ans);
}
return 0;
}
T3:
考场代码
//
/*
By:Luckyblock
*/
#include <algorithm>
#include <cstdio>
#include <ctype.h>
#include <cstring>
#define ll long long
const int kMaxn = 5e4 + 10;
const int kMod = 2019;
//=============================================================
int n, q, root, edge_num, head[kMaxn], u[kMaxn << 1], v[kMaxn << 1], w[kMaxn << 1], ne[kMaxn << 1];
int size[kMaxn], dep[kMaxn], fa[kMaxn], son[kMaxn], top[kMaxn];
int Dfn, dfn[kMaxn];
//=============================================================
inline ll read() {
ll f = 1, w = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
struct SegmentTree {
#define ls (now_<<1)
#define rs (now_<<1|1)
#define mid ((L_+R_)>>1)
int prod[kMaxn << 2], minus[kMaxn << 2];
void Modify(int now_, int L_, int R_, int ql_, int qr_, int p_, int m_) {
if (ql_ <= L_ && R_ <= qr_) {
prod[now_] += p_; prod[now_] %= kMod;
minus[now_] += m_; minus[now_] %= kMod;
return ;
}
if (ql_ <= mid) Modify(ls, L_, mid, ql_, qr_, p_, m_);
if (qr_ > mid) Modify(rs, mid + 1, R_, ql_, qr_, p_, m_);
}
int Query(int now_, int L_, int R_, int pos_, int size, int p_) {
p_ += prod[now_];
int m = minus[now_], ret;
if (L_ == R_) return (p_ * size % kMod - m)% kMod;
if (pos_ <= mid) ret = Query(ls, L_, mid, pos_, size, p_);
else ret = Query(rs, mid + 1, R_, pos_, size, p_);
return (ret - m + kMod) % kMod;
}
} t;
void AddEdge(int u_, int v_, int w_) {
u[++ edge_num] = u_, v[edge_num] = v_, w[edge_num] = w_;
ne[edge_num] = head[u_], head[u_] = edge_num;
}
void Dfs1(int u_, int fa_) {
size[u_] = 1;
fa[u_] = fa_;
dep[u_] = dep[fa_] + 1;
for (int i = head[u_]; i; i = ne[i]) {
int v_ = v[i];
if (v_ == fa_) continue ;
Dfs1(v_, u_);
size[u_] += size[v_];
if (size[son[u_]] < size[v_]) son[u_] = v_;
}
}
void Dfs2(int u_, int top_) {
dfn[u_] = ++ Dfn;
top[u_] = top_;
if (son[u_]) Dfs2(son[u_], top_);
for (int i = head[u_]; i; i = ne[i]) {
int v_ = v[i];
if (v_ == fa[u_] || v_ == son[u_]) continue ;
Dfs2(v_, v_);
}
}
void ModifyChain(int u_, int v_, int prod_, int minus_) {
for (; top[u_] != top[v_]; u_ = fa[top[u_]]) {
if (dep[top[u_]] < dep[top[v_]]) {
std :: swap(u_, v_);
}
t.Modify(1, 1, n, dfn[top[u_]], dfn[u_], prod_, minus_);
}
if (dep[u_] > dep[v_]) std :: swap(u_, v_);
t.Modify(1, 1, n, dfn[u_], dfn[v_], prod_, minus_);
}
void Modify(int u_, int v_, int w_) {
int tmpu = u_, tmpv = v_;
for (; top[tmpu] != top[tmpv]; tmpu = fa[top[tmpu]]) {
if (dep[top[tmpu]] < dep[top[tmpv]]) {
std :: swap(tmpu, tmpv);
}
}
int lca = (dep[tmpu] > dep[tmpv] ? tmpv : tmpu);
for (; u_ != lca; u_ = fa[u_]) {
ModifyChain(fa[u_], 1, w_ * size[u_] % kMod,
w_ * size[u_] * size[u_] % kMod);
}
for (; v_ != lca; v_ = fa[v_]) {
ModifyChain(fa[v_], 1, w_ * size[v_] % kMod,
w_ * size[v_] * size[v_] % kMod);
}
}
//=============================================================
int main() {
freopen("network.in", "r", stdin);
freopen("network.out", "w", stdout);
n = read(), q = read();
root = 1;
for (int i = 2; i <= n; ++ i) {
int x = read(), y = i, z = read();
AddEdge(x, y, z);
}
Dfs1(root, 0), Dfs2(root, root);
for (int i = 1; i <= edge_num; i ++) {
int x = u[i], y = v[i], z = w[i];
Modify(x, y, z);
}
while (q --) {
char opt[10]; scanf("%s", opt + 1);
if (opt[1] == 'I') {
int x = read(), y = read(), z = read();
Modify(x, y, z);
} else {
int x = read();
printf("%d\n", t.Query(1, 1, n, dfn[x], size[x], 0));
}
}
return 0;
}
/*
5 5
1 1
2 5
1 2
2 1
ASK 1
ASK 2
ASK 3
ASK 4
ASK 5
*/
/*
#include <algorithm>
#include <cstdio>
#include <ctype.h>
#include <cstring>
#define ll long long
const int kMaxn = 5e4 + 10;
//=============================================================
int root, edge_num, head[kMaxn], v[kMaxn << 1], w[kMaxn << 1], ne[kMaxn << 1];
//=============================================================
inline ll read() {
ll f = 1, w = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void AddEdge(int u_, int v_, int w_) {
v[++ edge_num] = v_, w[edge_num] = w_;
ne[edge_num] = head[u_], head[u_] = edge_num;
}
void Dfs(int u_, int fa_) {
for (int i = head[u_]; i; i = ne[i]) {
int v_ = v[i];
if (v_ == fa_) continue ;
}
}
//=============================================================
int main() {
int n = read(), q = read();
for (int i = 1; i < n; ++ i) {
int u = read(), v = i, w = read();
if (u == v) root = i;
AddEdge(u, v, w), AddEdge(v, u, w);
}
Dfs(root, 0);
return 0;
}
*/
正解
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <utility>
#include <vector>
using namespace std;
#define maxN 100050
#define Mod 2019
int N;
int fa[maxN], v[maxN];
vector<int> H[maxN];
int siz[maxN], top[maxN], son[maxN], dep[maxN];
int sum_siz[maxN], sum_siz2[maxN];
pair<int,int> govern[maxN];
namespace Segment_tree
{
struct Tnode
{
int s1, s2, add;
} tree[maxN * 4];
void Modify(int r, int x, int y, int p)
{
tree[r].s1 = (tree[r].s1 + p * (sum_siz[y] - sum_siz[x-1])) % Mod;
tree[r].s2 = (tree[r].s2 + p * (sum_siz2[y] - sum_siz2[x-1])) % Mod;
tree[r].add = (tree[r].add + p ) % Mod;
}
void Down(int r, int L, int R, int mid)
{
Modify((r<<1), L, mid, tree[r].add);
Modify(((r<<1)^1), mid+1, R, tree[r].add);
tree[r].add = 0;
}
void update(int r, int L, int R, int x, int y, int p)
{
if (x > R || y < L) return ;
if (L >= x && R <= y)
{
Modify(r, L, R, p);
return ;
}
int mid = (L+R) >> 1;
Down(r, L, R, mid);
update((r<<1), L, mid, x, y, p);
update(((r<<1)^1), mid+1, R, x, y, p);
tree[r].s1 = (tree[r<<1].s1 + tree[(r<<1)^1].s1) % Mod;
tree[r].s2 = (tree[r<<1].s2 + tree[(r<<1)^1].s2) % Mod;
}
pair<int,int> ask(int r, int L, int R, int x, int y)
{
if (x > R || y < L) return make_pair(0, 0);
if (L >= x && R <= y) return make_pair(tree[r].s1, tree[r].s2);
int mid = (L+R)>>1;
Down(r, L, R, mid);
pair<int,int> res1 = ask((r<<1), L, mid, x, y),
res2 = ask(((r<<1)^1), mid+1, R, x, y);
return make_pair((res1.first + res2.first) % Mod,
(res1.second + res2.second) % Mod);
}
void Update(int x, int y, int p)
{
update(1, 1, N, x, y, p);
}
pair<int,int> Ask(int x, int y)
{
return x <= y ? ask(1, 1, N, x, y) : make_pair(0, 0);
}
}
void Dfs(int r, int top_id, int &T)
{
top[r] = top_id;
govern[r].first = ++ T;
sum_siz[T] = (sum_siz[T - 1] + siz[r]) % Mod;
sum_siz2[T] = (sum_siz2[T - 1] + (siz[r]%Mod) * (siz[r]%Mod)) % Mod;;
if (son[r]) Dfs(son[r], top_id, T);
for (int i = 0; i < (int)H[r].size(); ++ i)
if (H[r][i] != son[r])
Dfs(H[r][i], H[r][i], T);
govern[r].second = T;
}
void Init()
{
static int q[maxN];
int head = 1, tail = 1;
q[1] = 1;
dep[1] = 1;
while (head <= tail)
{
int cur = q[head ++];
for (int i = 0; i < (int)H[cur].size(); ++ i)
{
dep[ H[cur][i] ] = dep[cur] + 1;
q[++tail] = H[cur][i];
}
}
for (int i = N; i >= 1; -- i)
{
int cur = q[i];
siz[cur] = 1;
son[cur] = 0;
for (int j = 0; j < (int)H[cur].size(); ++ j)
{
int nxt = H[cur][j];
siz[cur] += siz[nxt];
if (son[cur] == 0 || siz[nxt] > siz[son[cur]])
son[cur] = nxt;
}
}
int T = 0;
Dfs(1, 1, T);
}
void Update_path(int u, int v, int w)
{
if (top[u] == top[v])
{
if (u == v) return ;
if (dep[u] > dep[v]) swap(u, v);
Segment_tree::Update(govern[u].first+1, govern[v].first, w);
return ;
}
if (dep[ top[u] ] < dep[ top[v] ]) swap(u, v);
Segment_tree::Update(govern[top[u]].first, govern[u].first, w);
Update_path(fa[top[u]], v, w);
}
int main()
{
freopen("network.in", "r", stdin);
freopen("network.out", "w", stdout);
int Q;
scanf("%d%d", &N, &Q);
for (int i = 2; i <= N; ++ i)
{
scanf("%d%d", fa+i, v+i);
H[ fa[i] ].push_back(i);
}
Init();
for (int i = 2; i <= N; ++ i)
Segment_tree::Update(govern[i].first, govern[i].first, v[i]);
while (Q--)
{
char order[5];
scanf("%s", order);
if (order[0] == 'A')
{
int x;
scanf("%d", &x);
pair<int,int> tmp = Segment_tree::Ask(govern[x].first+1, govern[x].second);
int res = (siz[x] * tmp.first - tmp.second) % Mod;
res = (res + Mod) % Mod;
printf("%d\n", res);
}
else
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
Update_path(u, v, w);
}
}
return 0;
}