noip2021訓練4
SCOI2003 嚴格N元樹
高精度板子(?
不過 dp 方程還是挺難想到的
設 \(f_i\) 表示深度 \(\le i\) 的 \(n\) 元樹的個數,那麼答案即為 \(f_d-f_{d-1}\)
這就是差分的頂級理解
考慮如何轉移
對於 \(f_i\),可以理解成 \(f_{i-1}\) 再加上一個根,這個根有 \(n\) 個子樹,每個子樹都有 \(f_{i-1}\) 種方案,還有一種只有根的。
所以 \(f_i=f_{i-1}^n+1\)
然後套個高精度即可
Code
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; struct INT { int a[210]; INT() { memset(a, 0, sizeof(a)); } void rd() { char s[210]; scanf("%s", s); a[0] = strlen(s); for(int i = 1; i <= a[0]; i++) a[i] = s[a[0] - i] - '0'; return; } void print() { for(int i = a[0]; i >= 1; i--) cout << a[i]; cout << endl; return; } INT operator + (const INT &b) const { INT c; c.a[0] = max(a[0], b.a[0]); for(int i = 1; i <= c.a[0]; i++) { c.a[i] += a[i] + b.a[i]; c.a[i + 1] += c.a[i] / 10; c.a[i] %= 10; } if(c.a[c.a[0] + 1]) c.a[0]++; return c; } bool operator < (const INT &b) const { if(a[0] != b.a[0]) return a[0] < b.a[0]; for(int i = a[0]; i >= 1; i--) if(a[i] != b.a[i]) return a[i] < b.a[i]; return false; } INT operator - (INT b) const { INT t; memcpy(t.a, a, sizeof(a)); for(int i = 1; i <= t.a[0]; i++) { if(t.a[i] < b.a[i]) t.a[i] += 10, t.a[i + 1]--; t.a[i] -= b.a[i]; } while(t.a[0] > 1 && !t.a[t.a[0]]) t.a[0]--; return t; } INT operator * (const INT &b) const { INT c; for(int i = 1; i <= a[0]; i++) { int w = 0; for(int j = 1; j <= b.a[0]; j++) { c.a[i + j - 1] += a[i] * b.a[j] + w; w = c.a[i + j - 1] / 10; c.a[i + j - 1] %= 10; } c.a[i + b.a[0]] += w; } c.a[0] = a[0] + b.a[0]; if(!c.a[c.a[0]]) c.a[0]--; return c; } INT operator / (const int &b) const { INT c; memcpy(c.a, a, sizeof(a)); for(int i = a[0]; i > 1; i--) { c.a[i - 1] += (c.a[i] % b) * 10; c.a[i] /= b; } c.a[1] /= b; while(!c.a[c.a[0]]) c.a[0]--; return c; } void operator = (int b) { while(b) a[++a[0]] = b % 10, b /= 10; } } f[20]; int n, d; int main() { scanf("%d%d", &n, &d); f[0] = 1; for(int i = 1; i <= d; i++) { f[i] = 1; for(int j = 1; j <= n; j++) f[i] = f[i] * f[i - 1]; f[i] = f[i] + f[0]; } (f[d] - f[d - 1]).print(); return 0; } // A.S.
ZJOI2008 騎士
基環樹上 dp 板子題
只需要在環上斷一條邊,這條邊的兩個端點只能選一個,以兩個點為根分別 dp,然後將答案取個 \(max\)。
dp 的時候就是沒有上司的舞會
注意這是一個基環樹森林,所以要列舉一遍每個點。
Code
#include <bits/stdc++.h> #define ll long long #define INF 1e18 using namespace std; namespace IO { template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();} while(isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f; } template <typename T> void write(T x) { if(x < 0) putchar('-'), x = -x; if(x > 9) write(x / 10); putchar(x % 10 + '0'); } } using namespace IO; const int N = 1e6 + 5; int n, a[N], fa[N]; vector <int> g[N]; bool vis[N]; ll f[N][2], ans; void dp(int u, int rt) { vis[u] = 1; f[u][0] = 0, f[u][1] = a[u]; for(auto v : g[u]) { if(v == rt) { f[v][1] = -INF; continue; } dp(v, rt); f[u][0] += max(f[v][0], f[v][1]); f[u][1] += f[v][0]; } return; } void solve(int x) { vis[x] = 1; while(!vis[fa[x]]) { x = fa[x]; vis[x] = 1; } dp(x, x); ll t = max(f[x][0], f[x][1]); x = fa[x]; dp(x, x); ans += max(t, max(f[x][0], f[x][1])); return; } int main() { read(n); for(int i = 1; i <= n; i++) { read(a[i]), read(fa[i]); g[fa[i]].push_back(i); } for(int i = 1; i <= n; i++) if(!vis[i]) solve(i); write(ans), putchar('\n'); return 0; } // A.S.
JSOI2008 球形空間產生器
根據,\(d=\sqrt{\sum_{i=1}^n(a_i-b_i)^2}\)
可得,\(d^2=\sum_{i=1}^n(a_i-b_i)^2\)
化簡得,\(d^2=\sum_{i=1}^n(a_i^2+b_i^2-2a_ib_i)\)
移項得,\(\sum_{i=1}^n(-2a_ib_i)+r^2+\sum_{i=1}^nb_i^2=\sum_{i=1}^na_i^2\)
其中 \(b_i\) 是未知數
注意到,\(r^2+\sum_{i=1}^nb_i^2\) 在每個方程中都有,因此可以設為一個係數為 \(1\) 的未知數,這樣就會被消掉了。
然後就可以愉快的高斯消元了 OvO
Code
#include <bits/stdc++.h>
#define db double
using namespace std;
const int N = 15;
int n;
db a[N][N];
void gauss()
{
for(int i = 1; i <= n; i++)
{
if(!a[i][i])
{
int j;
for(j = i + 1; j <= n; j++)
if(a[j][i]) break;
for(int k = 1; k <= n + 1; k++)
swap(a[i][k], a[j][k]);
}
for(int k = i + 1; k <= n; k++)
for(int j = n + 1; j >= i; j--)
a[k][j] -= a[k][i] / a[i][i] * a[i][j];
}
for(int i = n; i >= 1; i--)
{
for(int j = i + 1; j <= n; j++)
a[i][n + 1] -= a[i][j] * a[j][n + 1];
a[i][n + 1] /= a[i][i];
}
return;
}
int main()
{
scanf("%d", &n), n++;
for (int i = 1; i <= n; ++ i)
{
for (int j = 1; j < n; ++ j)
{
scanf("%lf", &a[i][j]);
a[i][n + 1] -= a[i][j] * a[i][j];
a[i][j] *= -2.0;
}
a[i][n] = 1;
}
gauss();
for(int i = 1; i < n; ++ i){
printf("%.3lf",a[i][n + 1]);
if(i < n - 1) printf(" ");
}
}
// A.S.
NOI2015 軟體包管理器
樹剖板子題
鏈上修改查詢、子樹修改查詢
Code
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
const int N = 1e5 + 5;
int read()
{
int x = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x;
}
int n,m;
vector <int> G[N];
int dep[N],siz[N],son[N],fa[N];
void dfs1(int u,int f)
{
dep[u] = dep[f] + 1;
siz[u] = 1;
fa[u] = f;
for(int i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
if(v == f) continue;
dfs1(v,u);
siz[u] += siz[v];
if(siz[son[u]] < siz[v]) son[u] = v;
}
}
int cnt,id[N],top[N];
void dfs2(int u,int tf)
{
id[u] = ++cnt;
top[u] = tf;
if(son[u]) dfs2(son[u],tf);
for(int i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
if(v == fa[u] || v == son[u]) continue;
dfs2(v,v);
}
}
int sum[N << 2], tag[N << 2];
void pushup(int rt)
{
sum[rt] = sum[ls] + sum[rs];
}
void pushdown(int l,int r,int rt)
{
if(tag[rt] != -1)
{
int mid = (l + r) >> 1;
sum[ls] = tag[rt] * (mid - l + 1);
tag[ls] = tag[rt];
sum[rs] = tag[rt] * (r - mid);
tag[rs] = tag[rt];
tag[rt] = -1;
}
}
void update(int L,int R,int v,int l,int r,int rt)
{
if(l > R || r < L) return;
if(L <= l && r <= R)
{
sum[rt] = v * (r - l + 1);
tag[rt] = v;
return;
}
pushdown(l,r,rt);
int mid = (l + r) >> 1;
update(L,R,v,l,mid,ls);
update(L,R,v,mid + 1,r,rs);
pushup(rt);
}
void upd1(int x)
{
while(top[x] != 1)
{
update(id[top[x]], id[x], 1, 1, n ,1);
x = fa[top[x]];
}
update(id[top[x]], id[x], 1, 1, n, 1);
}
void upd2(int x)
{
update(id[x], id[x] + siz[x] - 1, 0, 1, n, 1);
}
int main()
{
n = read();
for(int i = 1; i < n; i++)
{
int u = read() + 1;
G[u].push_back(i + 1);
}
dfs1(1,0);
dfs2(1,1);
memset(tag,-1,sizeof(tag));
m = read();
while(m--)
{
char op[10];
scanf("%s",op);
int x = read() + 1, ans = sum[1];
if(op[0] == 'i')
{
upd1(x);
printf("%d\n",sum[1] - ans);
}
else
{
upd2(x);
printf("%d\n",ans - sum[1]);
}
}
return 0;
}
// A.S.
NOI2014 動物園
根據 \(kmp\) 求出的 \(nxt\) 陣列進行計算。
但是為了避免在 \(aaa...a\) 時複雜度退化為 \(O(n^2)\),可以像求 \(nxt\) 一樣用指標記錄一下,如果在 \(i-1\) 時合法,那麼在 \(i\) 時也合法。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
using namespace std;
const int N = 1e6 + 5;
const int p = 1e9 + 7;
char s[N];
int nxt[N],num[N];
signed main()
{
int T;
scanf("%lld",&T);
while(T--)
{
scanf("%s",s + 1);
int n = strlen(s + 1);
num[0] = 0, num[1] = 1;
memset(nxt,0,sizeof(nxt));
for(int i = 2, j = 0; i <= n; i++)
{
while(j && s[i] != s[j + 1]) j = nxt[j];
if(s[i] == s[j + 1]) j++;
nxt[i] = j;
num[i] = num[j] + 1;
}
int ans = 1;
for(int i = 1, j = 0; i <= n; i++)
{
while(j && s[i] != s[j + 1]) j = nxt[j];
if(s[i] == s[j + 1]) j++;
while(j && j * 2 > i) j = nxt[j];
ans = ans * (num[j] + 1) % p;
}
printf("%lld\n",ans);
}
return 0;
}
// A.S.
不想寫了,晚上回去再寫
$$A\ drop\ of\ tear\ blurs\ memories\ of\ the\ past.$$