2020牛客暑期多校訓練營(第七場)
目錄
Contest Info
Solved | A | B | C | D | E | F | G | H | I | J |
---|---|---|---|---|---|---|---|---|---|---|
6 / 10 | - | O | O | O | - | - | - | O | O | O |
- O 在比賽中通過
- Ø 賽後通過
- ! 嘗試了但是失敗了
- - 沒有嘗試
Solutions
B. Mask Allocation
類似於歐幾里得演算法的想法去構造即可,這樣可取到最大值。
Code
// Author : heyuhhh // Created Time : 2020/08/01 12:54:54 #include<bits/stdc++.h> #define MP make_pair #define fi first #define se second #define pb push_back #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() #define INF 0x3f3f3f3f using namespace std; typedef long long ll; typedef pair<int, int> pii; //head const int N = 1e5 + 5; void run() { int n, m; cin >> n >> m; vector<int> ans; while (n) { if (n < m) swap(n, m); for (int i = 1; i <= m; i++) { ans.push_back(m); } n -= m; } sort(all(ans)); reverse(all(ans)); cout << sz(ans) << '\n'; for (auto it : ans) { cout << it << ' '; } cout << '\n'; } int main() { #ifdef Local freopen("input.in", "r", stdin); #endif ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); int T; cin >> T; while(T--) run(); return 0; }
C. A National Pandemic
題意:
給定一顆無根樹,初始每個結點的權值為\(0\)。
然後有以下三種操作:
- 選定\(x\),然後每個點權值加上\(w-dist(x,y)\);
- \(F(x)=min(F(x),0)\);
- 詢問\(F(x)\)。
思路:
稍加思考可以發現,二操作就是賣萌的,我們記錄一個delta值就行。
考慮如何處理一操作:\(w-dist(x,y)=w-(deep(x)+deep(y)-2lca(x,y))\)。
那麼\(w,deep[x],deep[y]\)部分我們用全域性變數標記一下即可,主要是處理\(lca\)部分。
這一部分我們考慮差分來處理,即從每一個\(y\)
用樹剖來處理的話時間複雜度為\(O(nlog^2n)\)。
程式碼如下:
Code
// Author : heyuhhh
// Created Time : 2020/08/02 18:04:12
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
void err(int x) {cerr << x;}
void err(long long x) {cerr << x;}
void err(double x) {cerr << x;}
void err(char x) {cerr << '"' << x << '"';}
void err(const string &x) {cerr << '"' << x << '"';}
void _print() {cerr << "]\n";}
template<typename T, typename V>
void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
template<typename T>
void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
template <typename T, typename... V>
void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifdef Local
#define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define dbg(x...)
#endif
//head
const int N = 1e5 + 5;
vector<int> G[N];
int n, m;
// 注意bson的初始化,其餘可以自動初始化
// 注意每個點實際值為dfn[x]
int sz[N], deep[N], bson[N], ff[N];
int top[N], dfn[N], T;
void dfs(int u, int fa) {
deep[u] = deep[fa] + 1;
sz[u] = 1;
ff[u] = fa;
int Max = -1;
for (auto v : G[u]) {
if (v != fa) {
dfs(v, u);
sz[u] += sz[v];
if (sz[v] > Max) {
Max = sz[v];
bson[u] = v;
}
}
}
}
void dfs(int u, int fa, int topf) {
dfn[u] = ++T;
top[u] = topf;
if (bson[u] != 0) {
dfs(bson[u], u, topf);
}
for (auto v : G[u]) {
if (v != fa && v != bson[u]) {
dfs(v, u, v);
}
}
}
ll sumv[N << 2], lz[N << 2];
//區間加
void tag(int o, int l, int r, ll v) {
sumv[o] += 1ll * (r - l + 1) * v;
lz[o] += v;
}
void push_up(int o) {
sumv[o] = sumv[o << 1] + sumv[o << 1|1];
}
void push_down(int o, int l, int r) {
if(lz[o] != 0) {
int mid = (l + r) >> 1;
tag(o << 1, l, mid, lz[o]);
tag(o << 1|1, mid + 1, r, lz[o]);
lz[o] = 0;
}
}
void build(int o, int l, int r) {
lz[o] = 0;
if(l == r) {
sumv[o] = 0;
return;
}
int mid = (l + r) >> 1;
build(o << 1, l, mid), build(o << 1|1, mid + 1, r);
push_up(o);
}
void update(int o, int l, int r, int L, int R, int v) {
if(L <= l && r <= R) {
tag(o, l, r, v);
return;
}
push_down(o, l, r);
int mid = (l + r) >> 1;
if(L <= mid) update(o << 1, l, mid, L, R, v);
if(R > mid) update(o << 1|1, mid + 1, r, L, R, v);
push_up(o);
}
ll query(int o, int l, int r, int L, int R) {
if(L <= l && r <= R) {
return sumv[o];
}
push_down(o, l, r);
int mid = (l + r) >> 1;
ll res = 0;
if(L <= mid) res += query(o << 1, l, mid, L, R);
if(R > mid) res += query(o << 1|1, mid + 1, r, L, R);
return res;
}
ll add[N];
ll cnt, sumx;
ll query(int x) {
ll res = 0;
int t = x;
while (x != 0) {
res += query(1, 1, n, dfn[top[x]], dfn[x]);
x = ff[top[x]];
}
return res + add[t] - 1ll * cnt * deep[t] + sumx;
}
void modify(int x) {
while (x != 0) {
update(1, 1, n, dfn[top[x]], dfn[x], 2);
x = ff[top[x]];
}
}
void run() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
G[i].clear();
bson[i] = 0;
add[i] = 0;
}
T = sumx = cnt = 0;
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, 0);
dfs(1, 0, 1);
build(1, 1, n);
while (m--) {
int op;
cin >> op;
if (op == 1) {
int w, x;
cin >> x >> w;
++cnt;
sumx += w - deep[x];
modify(x);
} else if (op == 2) {
int x;
cin >> x;
ll res = query(x);
if (res > 0) {
add[x] -= res;
}
} else {
int x;
cin >> x;
ll res = query(x);
cout << res << '\n';
}
}
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T; while(T--)
run();
return 0;
}
也可以直接用點分樹來維護距離,這樣每次修改一個點過後,只會影響其餘每個點到它的logn條路徑,然後也是類似於差分的思想來計算答案。即當\(y\rightarrow f(y)\)時,我們需要統計來自另外子樹的貢獻,減去來自\(y\)這顆子樹的貢獻。所以修改值時要在每個父親結點打個正貢獻的標記,然後在他們父親處打個負貢獻標記即可。
直接copy別人的程式碼qaq。
Code
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define LC k<<1
#define RC k<<1|1
#define tm tttadt
typedef long long LL;
const int N=510000;
const int M=1100000;
const LL mod=1e9+7;
int T,n,m;
LL delta[N],sumw;
struct point_tree
{
vector<int> ch[N];
vector<int> g[N];
int f[N],sum,n,son[N],pp[N],nxt,root;
int up[N],dis[20][N],dep[N];
LL s1[20][N],s2[20][N];
void getroot(int x,int fa)
{
son[x]=1,f[x]=0;
for (int i=0;i<g[x].size();i++)
if (!pp[g[x][i]]&&g[x][i]!=fa)
{
getroot(g[x][i],x);
son[x]+=son[g[x][i]];
f[x]=max(f[x],son[g[x][i]]);
}
f[x]=max(f[x],sum-son[x]);
if (f[x]<f[nxt]) nxt=x;
}
void dfs(int x,int fa,int depth)
{
if (fa) dis[depth][x]=dis[depth][fa]+1;
else dis[depth][x]=0;
for (int i=0;i<g[x].size();i++)
if (!pp[g[x][i]]&&g[x][i]!=fa) dfs(g[x][i],x,depth);
}
void work(int x,int fa,int depth)
{
dep[x]=depth;
dfs(x,0,depth);
up[x]=fa;
pp[x]=1;
for (int i=0;i<g[x].size();i++)
if (!pp[g[x][i]])
{
nxt=0,sum=son[g[x][i]];
getroot(g[x][i],0);
ch[x].pb(nxt);
work(nxt,x,depth+1);
}
}
void change(int x)
{
int cur=x,depth=dep[x],last=0;
while (cur)
{
s1[depth][cur]+=dis[depth][x];
s2[depth][cur]+=1;
if (last) s1[depth][last]-=dis[depth][x],s2[depth][last]-=1;
last=cur;
cur=up[cur];
depth--;
}
}
LL ask(int x)
{
LL res=sumw-delta[x];
int cur=x,depth=dep[x],last=0;
while (cur)
{
res-=s1[depth][cur];
res-=s2[depth][cur]*dis[depth][x];
if (last) res-=s1[depth][last],res-=s2[depth][last]*dis[depth][x];
last=cur;
cur=up[cur];
depth--;
}
return res;
}
void init()
{
for (int i=1;i<=n;i++)
g[i].clear(),pp[i]=0;
memset(s1,0,sizeof(s1));
memset(s2,0,sizeof(s2));
for (int i=1;i<n;i++)
{
int x,y;
scanf("%d %d",&x,&y);
g[x].pb(y);
g[y].pb(x);
}
f[0]=sum=n;
nxt=0;
getroot(1,0);
root=nxt;
work(root,0,0);
}
}A;
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d %d",&n,&m);
sumw=0;
A.n=n;
A.init();
for (int i=1;i<=n;i++)
delta[i]=0;
while (m--)
{
int op;
scanf("%d",&op);
if (op==1)
{
int x,w;
scanf("%d %d",&x,&w);
A.change(x);
sumw+=w;
}
else if (op==2)
{
int x;
scanf("%d",&x);
LL val=A.ask(x);
if (val>0) delta[x]+=val;
}
else
{
int x;
scanf("%d",&x);
LL val=A.ask(x);
printf("%lld\n",val);
}
}
}
return 0;
}
D. Fake News
簽到。
Code
// Author : heyuhhh
// Created Time : 2020/08/01 12:06:53
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
ll n;
cin >> n;
if (n == 1 || n == 24) {
cout << "Fake news!" << '\n';
} else {
cout << "Nobody knows it better than me!" << '\n';
}
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T; while (T--)
run();
return 0;
}
H. Dividing
數論分塊即可。
I. Valuable Forests
題意:
\(n,n\leq 5000\)個點生成一個森林,森林的貢獻為\(\sum (d(i))^2,d(i)\)代表每個結點的度數。
問所有情況的貢獻之和為多少。
思路:
觀察範圍首先可以明確複雜度應該是\(O(n^2)\)的。
因為這是樹上計數問題,那麼我們容易想到prufer定理。
首先有:\(n\)個點組成的有標號無根樹,其個數為\(n^{n-2}\)。
然後\(n\)個點組成樹的度數貢獻之和為\(\displaystyle\sum_{i=1}^n\sum_{d=1}^{n-1}d^2{n-2\choose d-1}(n-1)^{n-2-d+1}\)。這一步也是根據prufer定理,因為定理內容則是將一顆樹與一個序列一一對應。
那麼剩下的便是一些基本的圖論上面dp的內容了,我們可以\(O(n^2)\)dp出\(f(n),F(n)\),分別表示\(n\)個點的森林有多少種情況,以及\(n\)個點的森林最終的答案為多少。這裡dp時有一個常見的思路,就是我們列舉\(i\)時,列舉\(i\)所在的環的大小,如果直接列舉環的大小可能會出問題。
另外還要注意算\(F\)時,要分別考慮兩部分的答案對另一部分貢獻的影響。
細節見程式碼:
Code
// Author : heyuhhh
// Created Time : 2020/08/05 20:10:05
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 5000 + 5;
int MOD;
int qpow(ll a, ll b) {
ll res = 1;
while(b) {
if(b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
int fac[N], inv[N];
void init() {
fac[0] = 1;
for(int i = 1; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
inv[N - 1] = qpow(fac[N - 1], MOD - 2);
for(int i = N - 2; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
}
int C(int n, int m) {
return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}
int ans[N];
int f[N], A[N], a[N];
void run() {
int T;
cin >> T >> MOD;
init();
a[1] = 1;
for (int i = 2; i < N; i++) {
a[i] = qpow(i, i - 2);
}
f[0] = 1;
for (int i = 1; i < N; i++) {
for (int j = 0; j < i; j++) {
f[i] = (f[i] + 1ll * C(i - 1, j) * f[i - j - 1] % MOD * a[j + 1] % MOD) % MOD;
}
}
A[1] = 0;
for (int i = 2; i < N; i++) {
for (int d = 1; d < i; d++) {
A[i] = (A[i] + 1ll * i * C(i - 2, d - 1) % MOD * d % MOD * d % MOD * qpow(i - 1, i - 2 - d + 1) % MOD) % MOD;
}
}
ans[1] = 0;
for (int i = 2; i < N; i++) {
for (int j = 0; j < i; j++) {
ans[i] = (ans[i] + 1ll * C(i - 1, j) * ((1ll * f[i - j - 1] * A[j + 1] % MOD
+ 1ll * ans[i - j - 1] * a[j + 1] % MOD) % MOD) % MOD) % MOD;
}
}
while (T--) {
int n;
cin >> n;
cout << ans[n] << '\n';
}
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
J. Pointer Analysis
把題意理解清楚過後按照要求傳遞閉包即可,一開始只求了一次wa飛了,後來多求幾次閉包就A了qaq
Code
// Author : heyuhhh
// Created Time : 2020/08/01 14:33:39
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
void err(int x) {cerr << x;}
void err(long long x) {cerr << x;}
void err(double x) {cerr << x;}
void err(char x) {cerr << '"' << x << '"';}
void err(const string &x) {cerr << '"' << x << '"';}
void _print() {cerr << "]\n";}
template<typename T, typename V>
void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
template<typename T>
void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
template <typename T, typename... V>
void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifdef Local
#define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define dbg(x...)
#endif
//head
const int N = 1000 + 5;
int mp[N][N];
void Floyd(int n) {
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
mp[i][j] |= (mp[i][k] & mp[k][j]);
}
}
}
}
void run() {
int n;
cin >> n;
vector<string> el(n), er(n);
for (int i = 0; i < n; i++) {
string t;
cin >> el[i] >> t >> er[i];
}
auto is_big = [&](char x) {
return 'A' <= x && x <= 'Z';
};
auto trans_big = [&](char x) {
return x - 'A';
};
auto trans_samll = [&](char x) {
return x - 'a' + 26;
};
for (int i = 0; i < n; i++) {
if (el[i].length() == 1 && er[i].length() == 1) {
int u, v;
if (is_big(el[i][0])) {
u = trans_big(el[i][0]);
} else {
u = trans_samll(el[i][0]);
}
if (is_big(er[i][0])) {
v = trans_big(er[i][0]);
} else {
v = trans_samll(er[i][0]);
}
// dbg(u, v);
mp[u][v] = 1;
}
}
int tot = 52;
for (int i = 0; i < N; i++) {
mp[i][i] = 1;
}
Floyd(tot);
int cnt = 10;
vector<vector<int>> val(52, vector<int>(26, -1));
while (cnt--) {
for (int i = 0; i < n; i++) {
int l1 = el[i].length(), l2 = er[i].length();
if (l1 == 1 && l2 == 1) continue;
if (l2 == 1) {
int u = trans_big(el[i][0]);
int v = trans_big(er[i][0]);
int t = trans_samll(el[i][2]) - 26;
for (int j = 26; j < 52; j++) {
if (mp[u][j]) {
if (val[j][t] == -1) val[j][t] = tot++;
mp[val[j][t]][v] = 1;
}
}
}
}
Floyd(tot);
for (int i = 0; i < n; i++) {
int l1 = el[i].length(), l2 = er[i].length();
if (l1 == 1 && l2 == 1) continue;
if (l1 == 1) {
int u = trans_big(el[i][0]);
int v = trans_big(er[i][0]);
int t = trans_samll(er[i][2]) - 26;
for (int j = 26; j < 52; j++) {
if (mp[v][j]) {
// dbg(t, u, val[j][t]);
if (val[j][t] == -1) val[j][t] = tot++;
mp[u][val[j][t]] = 1;
}
}
}
}
Floyd(tot);
}
vector<vector<char>> ans(26);
for (int i = 0; i < 26; i++) {
for (int j = 26; j < 52; j++) {
if (mp[i][j]) {
ans[i].push_back('a' + (j - 26));
}
}
sort(all(ans[i]));
}
for (int i = 0; i < 26; i++) {
cout << char('A' + i) << ": ";
for (auto& it : ans[i]) {
cout << it;
}
cout << '\n';
}
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}s