目錄
BZOJ2655 calc [ 生成函數 + DP + 拉格朗日差值 ]
[BZOJ4205][WOJ3643]卡牌配對 [網絡流 (巧妙建圖)]
WOJ3475 [POI2015] Pustynia [線段樹優化建圖]
[JSOI2007]文本生成器 [AC自動機 + DP]
我們考慮全集減補集, 也就是 26 ^ m - (一個單詞都沒有出現的次數)
對於後面可以在AC自動機上DP, f[i][j] 表示第i爲 在AC自動機上的 j 節點, 一次都沒有出現的方案數, 枚舉26個狀態轉移即可
#include<bits/stdc++.h>
#define N 6050
#define Mod 10007
using namespace std;
int n, m, f[105][N];
int ch[N][26], fail[N], vis[N], tot;
typedef long long ll;
void insert(string s){
int len = s.length(), now = 0;
for(int i=0; i<len; i++){
int x = s[i] - 'A';
if(!ch[now][x]) ch[now][x] = ++tot;
now = ch[now][x];
} vis[now] = 1;
}
void Build(){
queue<int> q;
for(int i=0; i<26; i++) if(ch[0][i]) q.push(ch[0][i]);
while(!q.empty()){
int x = q.front(); q.pop();
for(int i=0; i<26; i++){
if(!ch[x][i]) ch[x][i] = ch[fail[x]][i];
else fail[ch[x][i]] = ch[fail[x]][i], q.push(ch[x][i]), vis[ch[x][i]]|=vis[fail[ch[x][i]]];
}
}
}
void add(int &x, int y){ x = (x+y+Mod) % Mod;}
ll power(ll a, ll b){
ll ans = 1; for(;b;b>>=1){
if(b&1) ans = (ans*a) % Mod;
a = (a*a) % Mod;
} return ans;
}
int main(){
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++){
string s; cin>>s; insert(s);
} Build(); f[0][0] = 1;
for(int i=0; i<=m-1; i++)
for(int j=0; j<=tot; j++)
for(int k=0; k<26; k++)
if(!vis[ch[j][k]]) add(f[i+1][ch[j][k]], f[i][j]);
int ans = power(26, m);
for(int i=0; i<=tot; i++) add(ans, -f[m][i]);
printf("%d", ans); return 0;
}
BZOJ3687簡單題 [Bitset]
我們記錄每一個數的出現次數 0/1, 如果是1的話就把它異或上
於是 用Bitset 類似揹包一樣處理
#include<bits/stdc++.h>
#define N 2000050
using namespace std;
int n, ans; bitset<N> S;
int main(){
scanf("%d", &n); S[0] = 1;
for(int i=1; i<=n; i++){
int x; scanf("%d", &x);
S ^= (S<<x);
}
for(int i=1; i<=N-50; i++)
if(S[i]) ans ^= i;
printf("%d", ans); return 0;
}
恨 7 不成妻 [數位 DP]
首先一個數(合法)
從n位推到n+1位, 考慮如何算它的平方
於是乎直接維護 cnt (合法個數), sum (合法的數的和), sum2(合法數的平方的和)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Node{
ll cnt, sum, sum2;
}f[20][8][8];
int T, a[20]; ll L, R, fmul[20];
const int Mod = 1000000007;
ll add(ll a, ll b){ return (a+b) % Mod;}
ll mul(ll a, ll b){ return (a*b) % Mod;}
Node dfs(int u, int Mod1, int Mod2, int lim){
if(u == 0) return (Node){(Mod1 && Mod2), 0, 0};
Node &res = f[u][Mod1][Mod2];
if(res.cnt != -1 && !lim) return res;
Node ans = (Node){0, 0, 0}; int up = lim ? a[u] : 9;
for(int i=0; i<=up; i++){
if(i == 7) continue;
Node tmp = dfs(u-1, (Mod1 + i) % 7, (Mod2 * 10 + i) % 7, lim & (i == up));
ll now = mul(fmul[u], i);
ans.cnt = add(ans.cnt, tmp.cnt);
ans.sum = add(ans.sum, add(mul(now, tmp.cnt), tmp.sum));
ll A = mul(mul(now, now), tmp.cnt), B = mul(2, mul(now, tmp.sum)), C = tmp.sum2;
ans.sum2 = add(ans.sum2, add(A, add(B, C)));
}
if(!lim) f[u][Mod1][Mod2] = ans;
return ans;
}
ll Solve(ll x){
int cnt = 0; while(x) a[++cnt] = x % 10, x /= 10;
return dfs(cnt, 0, 0, 1).sum2;
}
int main(){
memset(f, -1, sizeof(f));
fmul[1]=1; for(int i=2; i<=20; i++) fmul[i] = mul(fmul[i-1], 10);
scanf("%d", &T);
while(T--){
scanf("%lld%lld", &L, &R);
printf("%lld\n", add(Solve(R), Mod-Solve(L-1)));
}
}
[SHOI2011]雙倍迴文 [Manacher]
跑馬拉車的時候, 如果 i - i的半徑超過了當前的中間點id, 那麼i這邊的迴文串可以直接根據id對稱過去形成一個新的迴文串
另外, 因爲要求長度爲偶數, 所以Manacher每次 +2
#include<bits/stdc++.h>
#define N 1000050
using namespace std;
char c[N], s[N]; int n, ans, p[N];
int main(){
scanf("%d%s", &n, c+1); n = 0;
int len = strlen(c+1);
for(int i=1; i<=len; i++){
s[++n] = '$'; s[++n] = c[i];
} s[++n] = '$';
for(int mx=0, id=0, i=1; i<=n; i+=2){
if(i < mx) p[i] = min(p[id * 2 - i], mx - i);
else p[i] = 1;
if(i < mx && i - p[i] < id) ans = max(ans, 2 * (i-id));
while(s[i-p[i]] == s[i+p[i]]) p[i]++;
if(i+p[i] > mx) id = i, mx = i+p[i];
} printf("%d", ans); return 0;
}
[NOI2007]貨幣兌換 [CDQ+斜率優化DP]
首先我們的策略是, 買了發現要賺, 那麼就買完
我們設 fi 爲第i天完了過後能最多剩下的前
變一下形
於是有了斜率優化的模型, 斜率取 ( - ai / bi ), 求能使 fi / bi 最大的點
但是 斜率不單調怎麼辦, 想到分治, 用[l -- mid]中的點 更新 [mid+1 -- r] 中的答案
插入需要按 x 排序, 查詢需要按斜率排序, 整個序列需要按時間排序
於是有了模型
void CDQ(int l, int r){
if(l == r){ f[l] = max(f[l], f[l-1]); return;}
int mid = (l+r) >> 1;
Solve(l, mid)
sort(l, mid, X);
sort(mid+1, r, K);
for(int i=l; i<=mid; i++) 插入凸包
for(int i=mid+1; i<=r; i++) 更新答案
sort(l, r, 時間)
Solve(mid+1, r)
}
排序用歸併排序的思想可以少一個log
#include<bits/stdc++.h>
#define N 100050
using namespace std;
const double eps = 1e-9;
int n, q[N]; double f[N];
struct Node{ double a, b, rate, k, x, y; int id;} t[N], tmp[N];
bool cmp(Node a, Node b){ return a.k > b.k;}
double slope(int i, int j){
if(fabs(t[i].x - t[j].x) < eps) return 1e18;
return (t[i].y - t[j].y) / (t[i].x - t[j].x);
}
void Solve(int l, int r){
if(l == r){
f[l] = max(f[l], f[l-1]);
t[l].y = f[l] / (t[l].rate * t[l].a + t[l].b);
t[l].x = t[l].y * t[l].rate; return;
}
int mid = (l+r) >> 1, ql = l-1, qr = mid;
for(int i=l; i<=r; i++) if(t[i].id <= mid) tmp[++ql] = t[i]; else tmp[++qr] = t[i];
for(int i=l; i<=r; i++) t[i] = tmp[i];
Solve(l, mid);
int L = 1, R = 0;
for(int i=l; i<=mid; i++){
while(R > 1 && slope(q[R-1], q[R]) < slope(q[R], i) + eps) R--;
q[++R] = i;
}
for(int i=mid+1; i<=r; i++){
while(L < R && slope(q[L], q[L+1]) + eps > t[i].k) L++;
f[t[i].id] = max(f[t[i].id], t[q[L]].x * t[i].a + t[q[L]].y * t[i].b);
}
Solve(mid+1, r);
ql = l, qr = mid+1;
for(int i=l; i<=r; i++){
if((t[ql].x < t[qr].x || qr > r || fabs(t[ql].x - t[qr].x) < eps) && ql <= mid)
tmp[i] = t[ql++];
else tmp[i] = t[qr++];
}
for(int i=l; i<=r; i++) t[i] = tmp[i];
}
int main(){
scanf("%d%lf", &n, &f[0]);
for(int i=1; i<=n; i++){
scanf("%lf%lf%lf", &t[i].a, &t[i].b, &t[i].rate);
t[i].k = - t[i].a / t[i].b; t[i].id = i;
} sort(t+1, t+n+1, cmp); Solve(1, n);
printf("%0.3lf", f[n]); return 0;
}
BZOJ2655 calc [ 生成函數 + DP + 拉格朗日差值 ]
先弄出答案的生成函數
這個多項式的第 n 項就是答案
考慮DP來求, f[i][j] 表示考慮到 ai, 多項式第 j 項 的值
看到 A 那麼大, 應該想到拉格朗日差值, 但這個東西是個多項式嗎, 繼續化簡看看
多項式就比較明顯可以看出來, 乘一個(k+1) 會多一次, 求一個前綴和會多一次, 所以答案是一個 n * 2 次的多項式
#include<bits/stdc++.h>
#define N 1050
using namespace std;
typedef long long ll;
int A, n, Mod;
ll fac[N], f[N][N], y[N];
ll add(ll a, ll b){ return ((a+b) % Mod + Mod) % Mod;}
ll mul(ll a, ll b){ return (a*b) % Mod;}
ll power(ll a, ll b){ ll ans = 1;
for(;b;b>>=1){ if(b&1) ans = mul(ans, a); a = mul(a, a);}
return ans;
}
ll lagrange(int k, int n){
if(n <= k) return y[n];
ll pre = 1, ans = 0;
for(int i=1; i<=k; i++) pre = mul(pre, n-i);
for(int i=1; i<=k; i++){
ll inv1 = power(mul(fac[k-i], fac[i-1]), Mod-2);
ll inv2 = power(n-i, Mod-2);
ll flag = ((k-i) & 1) ? -1 : 1;
ans = add(ans, flag * mul(mul(inv1, inv2), mul(pre, y[i])));
} return ans;
}
int main(){
scanf("%d%d%d", &A, &n, &Mod);
fac[0] = fac[1] = 1;
for(int i=2; i<=N-50; i++) fac[i] = mul(fac[i-1], i);
f[0][0] = 1;
for(int i=1; i<=2*n+1; i++){
f[i][0] = 1;
for(int j=1; j<=n; j++){
f[i][j] = add(f[i-1][j], mul(f[i-1][j-1], i));
}
}
for(int i=1; i<=n*2+1; i++) y[i] = f[i][n];
ll ans = lagrange(n*2+1, A);
printf("%lld", mul(ans, fac[n]));
return 0;
}
P3401 洛谷樹 [樹鏈剖分]
首先可以想到, 求出每個點到根的異或和, 然後枚舉兩個點算貢獻就可以了
然後發現每個數 < 1023, 明擺着讓你拆成每一位考慮, 然後估價就是線段樹維護0/1序列什麼的
所以現在可以先dfs一遍求出異或和, 然後對異或和按位考慮
哪些點對會產生貢獻 ? 只有一個0, 一個1, 那麼樹剖求出0, 1 的數量, 乘一下就可以了
修改一條邊只會影響子樹的異或和, 線段樹區間修改(0/1翻轉) 就可以了
#include<bits/stdc++.h>
#define N 60050
using namespace std;
int first[N], nxt[N], to[N], w[N], tot;
void add(int x, int y, int z){
nxt[++tot] = first[x], first[x] = tot;
to[tot] = y, w[tot] = z;
}
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
int n, m, a[N], val[N];
int siz[N], son[N], fa[N], dep[N], sum[N];
int top[N], id[N], pre[N], sign;
void dfs1(int u, int f){
siz[u] = 1;
for(int i=first[u];i;i=nxt[i]){
int t = to[i]; if(t == f) continue;
fa[t] = u; dep[t] = dep[u] + 1; sum[t] = sum[u] ^ w[i];
val[t] = w[i];
dfs1(t, u); siz[u] += siz[t];
if(siz[t] > siz[son[u]]) son[u] = t;
}
}
void dfs2(int u, int Top){
id[u] = ++sign; pre[sign] = u; top[u] = Top;
if(son[u]) dfs2(son[u], Top);
for(int i=first[u];i;i=nxt[i]){
int t = to[i]; if(t == fa[u] || t == son[u]) continue;
dfs2(t, t);
}
}
struct Ak{
int sum[N<<2], tag[N<<2];
void Pushup(int x){ sum[x] = sum[x<<1] + sum[x<<1|1]; }
void Build(int x, int l, int r){
if(l == r){ sum[x] = a[pre[l]]; return;}
int mid = (l+r) >> 1; Build(x<<1, l, mid); Build(x<<1|1, mid+1, r);
Pushup(x);
}
void Pushtag(int x, int l, int r){
tag[x] ^= 1; sum[x] = r - l + 1 - sum[x];
}
void Pushdown(int x, int l, int r){
if(tag[x]){
int mid = (l+r) >> 1;
Pushtag(x<<1, l, mid); Pushtag(x<<1|1, mid+1, r);
tag[x] = 0;
}
}
void Modify(int x, int l, int r, int L, int R){
if(L<=l && r<=R){ Pushtag(x, l, r); return;}
Pushdown(x, l, r); int mid = (l+r) >> 1;
if(L<=mid) Modify(x<<1, l, mid, L, R);
if(R>mid) Modify(x<<1|1, mid+1, r, L, R);
Pushup(x);
}
int Qu(int x, int l, int r, int L, int R){
if(L<=l && r<=R) return sum[x];
Pushdown(x, l, r); int mid = (l+r) >> 1, ans = 0;
if(L<=mid) ans += Qu(x<<1, l, mid, L, R);
if(R>mid) ans += Qu(x<<1|1, mid+1, r, L, R);
return ans;
}
int Quary(int u, int v){
int sum1 = 0, x = u, y = v;
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
sum1 += Qu(1, 1, n, id[top[u]], id[u]);
u = fa[top[u]];
} if(id[u] > id[v]) swap(u, v);
sum1 += Qu(1, 1, n, id[u], id[v]); // lca -> u
int sum = dep[x] + dep[y] - dep[u] * 2 + 1;
return (sum - sum1) * sum1;
}
}A[10];
int main(){
n = read(), m = read();
for(int i=1; i<n; i++){
int x = read(), y = read(), z = read();
add(x, y, z); add(y, x, z);
} dep[1] = 1; dfs1(1, 0); dfs2(1, 1);
for(int k=0; k<=9; k++){
for(int i=1; i<=n; i++) a[i] = (sum[i] >> k) & 1;
A[k].Build(1, 1, n);
}
while(m--){
int op = read(), u = read(), v = read();
if(op == 1){
long long ans = 0;
for(int k=0; k<=9; k++) ans += 1ll * (1<<k) * A[k].Quary(u, v);
printf("%lld\n", ans);
}
if(op == 2){
int w = read(); if(fa[u] != v) swap(u, v);
for(int k=0; k<=9; k++){
if(((w ^ val[u]) >> k) & 1) A[k].Modify(1, 1, n, id[u], id[u] + siz[u] - 1);
} val[u] = w;
}
} return 0;
}
[BZOJ4205][WOJ3643]卡牌配對 [網絡流 (巧妙建圖)]
首先 可以暴力 n^2 匹配 ... 然後一籌莫展
好, 先不考慮3個, 考慮1個, 我們加一列點分別代表(2, 3, 5, 7, 11...)(共46個), 然後左邊這一列向它的質因子連邊
例如 30 向 2, 3, 5 連邊, 右邊也同理, 邊權爲inf, 然後跑最大流
這樣做顯然是正確的, 因爲左邊要到右邊, 必須經過中間的點, 也就是說不互質
再考慮兩個, 嗯, 我們建 46 * 46個點不就完了, 比如說 (2, 3)這個點表示A類爲2的倍數, B類爲3的倍數
這樣做就做到了對邊分類一起建, 而不是n ^ 2 一個一個連邊
在來3個, 題目要求至少兩個不互質, 所以要麼(A,B), 要麼(B, C), 要麼(A,C)
於是我們建 3 * 46 * 46 個虛點表示上述情況, 此題完
#include<bits/stdc++.h>
#define N 5000050
using namespace std;
int first[N], nxt[N], to[N], w[N], tot = 1;
void add(int x, int y, int z){
nxt[++tot] = first[x], first[x] = tot, to[tot] = y, w[tot] = z;
nxt[++tot] = first[y], first[y] = tot, to[tot] = x, w[tot] = 0;
}
const int inf = 0x3fffffff;
int Map[3][50][50];
int prim[50] = {
1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157,
163, 167, 173, 179, 181, 191, 193, 197, 199
};
vector<int> v[205];
int n, m, st, ed, dis[N];
bool bfs(){
queue<int> q; q.push(st);
memset(dis, -1, sizeof(dis)); dis[st] = 0;
while(!q.empty()){
int x = q.front(); q.pop();
for(int i=first[x];i;i=nxt[i]){
int t = to[i]; if(dis[t] == -1 && w[i]){
dis[t] = dis[x] + 1; q.push(t);
if(t == ed) return true;
}
}
} return false;
}
int dfs(int u, int flow){
if(u == ed) return flow;
int ans = 0;
for(int i=first[u];i;i=nxt[i]){
int t = to[i]; if(dis[t] == dis[u] + 1){
int delta = dfs(t, min(flow, w[i]));
w[i] -= delta; w[i^1] += delta;
ans += delta; flow -= delta;
if(!flow) break;
}
} if(flow) dis[u] = -1;
return ans;
}
int dinic(){ int ans = 0; while(bfs()) ans += dfs(st, inf); return ans;}
int main(){
scanf("%d%d", &n, &m);
for(int i=2; i<=200; i++)
for(int j=1; j<=46; j++)
if(i % prim[j] == 0) v[i].push_back(j);
for(int i=0, c=m+n; i<3; i++)
for(int j=1; j<=46; j++)
for(int k=1; k<=46; k++)
Map[i][j][k] = ++c;
st = 0, ed = n+m+46*46*3+1;
for(int i=1; i<=n; i++){
add(st, i, 1);
int a, b, c; scanf("%d%d%d", &a, &b, &c);
for(int j=0; j<v[a].size(); j++)
for(int k=0; k<v[b].size(); k++)
add(i, Map[0][v[a][j]][v[b][k]], inf);
for(int j=0; j<v[b].size(); j++)
for(int k=0; k<v[c].size(); k++)
add(i, Map[1][v[b][j]][v[c][k]], inf);
for(int j=0; j<v[c].size(); j++)
for(int k=0; k<v[a].size(); k++)
add(i, Map[2][v[c][j]][v[a][k]], inf);
}
for(int i=1; i<=m; i++){
add(i+n, ed, 1);
int a, b, c; scanf("%d%d%d", &a, &b, &c);
for(int j=0; j<v[a].size(); j++)
for(int k=0; k<v[b].size(); k++)
add(Map[0][v[a][j]][v[b][k]], i+n, inf);
for(int j=0; j<v[b].size(); j++)
for(int k=0; k<v[c].size(); k++)
add(Map[1][v[b][j]][v[c][k]], i+n, inf);
for(int j=0; j<v[c].size(); j++)
for(int k=0; k<v[a].size(); k++)
add(Map[2][v[c][j]][v[a][k]], i+n, inf);
} printf("%d", dinic());
return 0;
}
[vijos1891]學姐的逛街計劃 [網絡流]
首先原點給1分配k的流量, 如果1號點不選, 全部傳給2, 於是1向2連費用爲0, 流量爲inf的邊
如果1選了, 1用掉的流量要6才能用, 與1向6連費用爲w1, 流量爲1的邊, 然後就是最大費用最大流
#include<bits/stdc++.h>
#define N 100050
using namespace std;
int first[N], nxt[N], to[N], w[N], c[N], tot = 1;
void add(int x, int y, int z, int v){
nxt[++tot] = first[x], first[x] = tot, to[tot] = y, w[tot] = z; c[tot] = v;
nxt[++tot] = first[y], first[y] = tot, to[tot] = x, w[tot] = 0; c[tot] = -v;
}
int n, k, a[N], st, ed;
int dis[N], vis[N], from[N], froms[N];
bool spfa(){
queue<int> q; q.push(st);
memset(dis, 63, sizeof(dis)); dis[st] = 0; vis[st] = 1;
int Inf = dis[1];
while(!q.empty()){
int x = q.front(); vis[x] = 0; q.pop();
for(int i=first[x];i;i=nxt[i]){
int t = to[i]; if(w[i] && dis[t] > dis[x] + c[i]){
dis[t] = dis[x] + c[i]; from[t] = x; froms[t] = i;
if(!vis[t]) q.push(t), vis[t] = 1;
}
}
} return dis[ed] != Inf;
}
int calc(){
int now = ed, flow = 1e9;
while(now != st){
flow = min(flow, w[froms[now]]);
now = from[now];
} now = ed;
while(now != st){
w[froms[now]] -= flow;
w[froms[now] ^ 1] += flow;
now = from[now];
} return flow;
}
int dinic(){
int ans = 0; while(spfa()) ans += calc() * dis[ed]; return ans;
}
int main(){
scanf("%d%d", &n, &k); st = 0, ed = n*3+1;
for(int i=1; i<=n*3; i++) scanf("%d", &a[i]);
for(int i=1; i<=n*3-1; i++) add(i, i+1, k, 0);
for(int i=1; i<=n*2; i++) add(i, i+n, 1, -a[i]);
for(int i=n*2+1; i<=n*3; i++) add(i, ed, 1, -a[i]);
add(st, 1, k, 0); add(n*3, ed, k, 0);
printf("%d", -dinic()); return 0;
}
WOJ3475 [POI2015] Pustynia [線段樹優化建圖]
我開始想, 這k個點每個都向logn個線段樹的點連邊不會炸嗎, 後來才發現可以建虛點
虛點 向 當前x連邊(權值爲0), 線段樹上的點向虛點連邊(權值爲1), 然後拓撲排序就可以了
// 虛點 + 線段樹優化建圖
#include<bits/stdc++.h>
#define N 2000050
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch=='-') f = -1;}
while(isdigit(ch)) cnt = cnt * 10 + (ch-'0'), ch = getchar();
return cnt * f;
}
int first[N], nxt[N], to[N], w[N], tot, du[N];
void add(int x, int y, int z){
nxt[++tot] = first[x], first[x] = tot;
to[tot] = y, w[tot] = z; du[y]++;
}
int n, s, m, cnt, a[N], f[N];
int ls[N], rs[N], rt;
void Build(int &x, int l, int r){
if(l == r){ x = l; return;} x = ++cnt;
int mid = (l+r) >> 1; Build(ls[x], l, mid); Build(rs[x], mid+1, r);
add(ls[x], x, 0); add(rs[x], x, 0);
}
void Modify(int x, int l, int r, int L, int R, int u, int v){
if(L>R) return;
if(L<=l && r<=R){ add(x, u, v); return;}
int mid = (l+r) >> 1;
if(L<=mid) Modify(ls[x], l, mid, L, R, u, v);
if(R>mid) Modify(rs[x], mid+1, r, L, R, u, v);
}
void topsort(){
queue<int> q; int res = 0;
for(int i=1; i<=cnt; i++) if(!du[i]) q.push(i);
while(!q.empty()){
int x = q.front(); q.pop(); res++;
if(f[x] > 1e9){ printf("NIE"); exit(0);}
if(f[x] > a[x] && a[x]){ printf("NIE"); exit(0);}
f[x] = max(f[x], a[x]);
for(int i=first[x];i;i=nxt[i]){
int t = to[i]; du[t]--;
if(!du[t]) q.push(t);
f[t] = max(f[t], f[x] + w[i]);
}
} if(res < cnt){ printf("NIE"); exit(0);}
}
int main(){
n = cnt = read(), s = read(), m = read();
for(int i=1; i<=s; i++){
int p = read(), v = read(); a[p] = v;
} Build(rt, 1, n);
for(int i=1; i<=m; i++){
int l = read(), r = read(), k = read(); cnt++;
for(int i=1; i<=k; i++){
int x = read();
add(cnt, x, 0);
Modify(rt, 1, n, l, x-1, cnt, 1); l = x+1;
} if(l <= r) Modify(rt, 1, n, l, r, cnt, 1);
} topsort();
printf("TAK\n");
for(int i=1; i<=n; i++) printf("%d ", f[i]);
return 0;
}
我們的 CPU 遭到攻擊 [LCT]
LCT 不支持連邊, 只能將邊轉成點表示, 做了QTREE發現此類LCT有一個套路 ---- 維護到當前Splay最淺的點的答案, 與最深的點的答案, 以下用sumL, sumR 表示
這是Splay維護的一條鏈
所有黑點到1的和 sumL(4) = sumL(ls(4)) + siz(rs(4)) * 邊權和(ls(4)) + siz(虛子樹) * 邊權和(ls(4))
sumR 同理, 然後就是LCT維護子樹操作了, 一堆細節, reverse的時候記得翻轉sumL, sumR
#include<bits/stdc++.h>
#define N 1000050
using namespace std;
typedef long long ll;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
int n, m, k, tot;
struct Node{
int ch[2], fa, siz, Siz, col;
ll val, sumE, Sum, sumL, sumR;
bool rev;
}t[N];
#define ls t[x].ch[0]
#define rs t[x].ch[1]
bool isRoot(int x){ return t[t[x].fa].ch[0] != x && t[t[x].fa].ch[1] != x;}
void Pushup(int x){
t[x].siz = t[ls].siz + t[rs].siz + t[x].col + t[x].Siz;
t[x].sumE = t[ls].sumE + t[rs].sumE + t[x].val;
t[x].sumL = t[ls].sumL + t[rs].sumL + t[x].Sum + (t[ls].sumE + t[x].val) * (t[rs].siz + t[x].Siz + t[x].col);
t[x].sumR = t[ls].sumR + t[rs].sumR + t[x].Sum + (t[rs].sumE + t[x].val) * (t[ls].siz + t[x].Siz + t[x].col);
}
void Pushrev(int x){ if(!x) return; swap(ls, rs); swap(t[x].sumL, t[x].sumR); t[x].rev ^= 1;}
void Pushdown(int x){
if(t[x].rev) Pushrev(ls), Pushrev(rs), t[x].rev = 0;
}
void rotate(int x){
int y = t[x].fa, z = t[y].fa, k = t[y].ch[1] == x;
if(!isRoot(y)) t[z].ch[t[z].ch[1] == y] = x; t[x].fa = z;
t[y].ch[k] = t[x].ch[k^1]; t[t[x].ch[k^1]].fa = y;
t[x].ch[k^1] = y; t[y].fa = x; Pushup(y); Pushup(x);
}
void Pushpath(int x){if(!isRoot(x)) Pushpath(t[x].fa); Pushdown(x);}
void Splay(int x){
Pushpath(x); while(!isRoot(x)){
int y = t[x].fa, z = t[y].fa;
if(!isRoot(y)) rotate((t[y].ch[0] == x) ^ (t[z].ch[0] == y) ? x : y);
rotate(x);
} Pushup(x);
}
void Access(int x){
for(int y = 0; x; y = x, x = t[x].fa){
Splay(x);
if(rs) t[x].Siz += t[rs].siz, t[x].Sum += t[rs].sumL;
rs = y;
if(rs) t[x].Siz -= t[rs].siz, t[x].Sum -= t[rs].sumL;
Pushup(x);
}
}
void Makeroot(int x){ Access(x); Splay(x); Pushrev(x);}
void Split(int x, int y){ Makeroot(x); Access(y); Splay(y);}
void Link(int u, int v){
Makeroot(u); Makeroot(v);
t[v].Siz += t[u].siz;
t[v].Sum += t[u].sumL;
t[u].fa = v; Pushup(v);
}
void link(int u, int v, int w){
t[++tot].val = w; Link(u, tot); Link(tot, v);
}
void Cut(int u, int v){
Split(u, v);
if(t[u].fa == v){ t[u].ch[1] = 0; Pushup(u);}
else t[u].fa = t[t[u].fa].ch[0] = 0;
t[v].ch[0] = t[t[v].ch[0]].fa = 0;
Pushup(v);
}
ll Quary(int x){ Makeroot(x); return t[x].sumL;}
int main(){
n = tot = read(), m = read(), k = read();
for(int i=1; i<=m; i++){
int u = read(), v = read(), w = read();
link(u, v, w);
}
while(k--){
char op[3]; scanf("%s", op);
if(op[0] == 'L'){
int u = read(), v = read(), w = read();
link(u, v, w);
}
if(op[0] == 'C'){
int u = read(), v = read(); Cut(u, v);
}
if(op[0] == 'F'){
int x = read(); Makeroot(x); t[x].col ^= 1; Pushup(x);
}
if(op[0] == 'Q'){
int x = read(); printf("%lld\n", Quary(x));
}
} return 0;
}