組合數問題
很明顯最下面的中間最大
把它放進堆,然後每次取最大,將它左右上分別放進堆中就可以了
現在的難題轉換爲求大小
一個套路就取 乘法轉加法
#include<bits/stdc++.h>
#define N 1000050
using namespace std;
typedef long long ll;
const int Mod = 1000000007;
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, k;
ll fac[N], ifac[N], a[N];
bool cmp(ll a, ll b){ return a > b;}
ll mul(ll a, ll b){ return (a * b) % Mod;}
ll add(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 C(int n, int m){ return mul(fac[n], mul(ifac[n-m], ifac[m]));}
typedef long double ld;
ld sum[N];
map<pair<int,int>, int> vis;
#define mp make_pair
struct Node{
int x, y;
friend bool operator < (const Node &a, const Node &b){
return sum[a.x] + sum[b.y] + sum[b.x - b.y] < sum[b.x] + sum[a.y] + sum[a.x - a.y];
}
} b[N];
priority_queue<Node> q;
int main(){
n = read(), k = read();
fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
for(int i = 2; i <= n; i++) fac[i] = mul(fac[i-1], i);
ifac[n] = power(fac[n], Mod-2);
for(int i = n-1; i >= 2; i--) ifac[i] = mul(ifac[i+1], i+1);
sum[0] = sum[1] = log(1);
for(int i = 2; i <= n; i++) sum[i] = sum[i-1] + log(i);
ll ans = 0; int cnt = 0;
q.push((Node){n, n/2});
while(cnt < k){
Node now = q.top(); q.pop();
if(vis[mp(now.x, now.y)]) continue;
vis[mp(now.x, now.y)] = 1;
ans = add(ans, C(now.x, now.y));
++cnt;
q.push((Node){now.x - 1, now.y});
q.push((Node){now.x, now.y - 1});
q.push((Node){now.x, now.y + 1});
} cout << ans; return 0;
}
字符串
首先有個暴力的莫隊做法,就是先建好 ,然後每次插入刪除字符串可以看做樹上的鏈加
複雜度
注意到,於是可以把所有字符串拼在一起然後莫隊,中途需要用維護一下
插入一個點,就是查前驅後繼,減去大區間的貢獻,加上兩邊區間的貢獻
複雜度 ,然後涼了
考慮用鏈表代替 ,應爲鏈表不支持插入插入,支持刪除,撤銷,所以我們想到了一種回滾莫隊的東西
實現如下----左端點相同按右端點從大到小排序,否則按左端點的塊排序
然後按照塊一塊一塊地處理
關於右端點的移動,每次從 n 往前挪,鏈表刪除就是了
關於左端點的移動,每次撤回到塊頭,然後刪除到
右端點每塊要移 n 次,是的,左端點每個詢問最多移次,也是的
#include<bits/stdc++.h>
#define N 300050
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;
}
typedef long long ll;
int ch[N][26], tot;
int dep[N], pos[N], val[N], node;
int n, m; ll F[N];
ll A, B, C;
int v[N], mxlen, len[N];
int g[N];
ll sum, ans[N]; int pre[N], suf[N];
int bel[N], seq[N], top, cnt[N];
struct Node{ int l, r, id;} q[N];
bool cmp(Node a, Node b){
if(bel[a.l] == bel[b.l]) return a.r > b.r;
return bel[a.l] < bel[b.l];
}
void ins(string s, int v){
int len = s.length(), now = 0;
for(int i = 0; i < len; i++){
int c = s[i] - 'a';
if(!ch[now][c]){ ch[now][c] = ++tot;}
dep[ch[now][c]] = dep[now] + 1;
now = ch[now][c]; pos[++node] = now; val[node] = v;
}
}
bool ck(ll f, ll len){ if(f == 0) return 0; return B * f + len * A >= C;}
ll calc(int l, int r){ ll len = r-l+1; return len * (len+1) / 2;}
ll gcd(ll a, ll b){ return !b ? a : gcd(b, a % b);}
void add(int i){
int x = pos[i];
if(!ck(F[x], dep[x]) && ck(F[x] + val[i], dep[x]) && !cnt[g[dep[x]]]++){
int k = g[dep[x]], l = pre[k], r = suf[k];
suf[l] = pre[r] = k; sum -= calc(l+1, r-1); sum += calc(l+1, k-1); sum += calc(k+1, r-1);
} F[x] += val[i];
}
void del(int i){
int x = pos[i];
if(ck(F[x], dep[x]) && !ck(F[x] - val[i], dep[x]) && !(--cnt[g[dep[x]]])){
int k = g[dep[x]], l = pre[k], r = suf[k];
suf[l] = r; pre[r] = l; sum -= calc(l+1, k-1); sum -= calc(k+1, r-1); sum += calc(l+1, r-1);
} F[x] -= val[i];
}
int main(){
n = read(), A = read(), B = read(), C = read();
for(int i = 1; i <= n; i++) v[i] = read();
for(int i = 1; i <= n; i++){
string s; cin >> s; len[i] = s.length();
mxlen = max(mxlen, len[i]);
ins(s, v[i]); len[i] += len[i-1];
}
for(int i = 1; i <= mxlen; i++) g[i] = read();
int siz = sqrt(node);
for(int i = 1; i <= node; i++) bel[i] = (i-1) / siz + 1;
m = read();
for(int i = 1; i <= m; i++){
int l = read(), r = read();
q[i].l = len[l-1] + 1, q[i].r = len[r], q[i].id = i;
} sort(q+1, q+m+1, cmp);
for(int i = 1; i <= node; i++){
int x = pos[i];
if(!ck(F[x], dep[x]) && ck(F[x] + val[i], dep[x]) && !cnt[g[dep[x]]]++){
seq[++top] = g[dep[x]];
} F[x] += val[i];
} seq[++top] = 0; seq[++top] = mxlen+1;
sort(seq+1, seq+top+1);
for(int i = 1; i <= top; i++) pre[seq[i]] = seq[i-1], suf[seq[i]] = seq[i+1];
for(int i = 2; i <= top; i++) sum += calc(seq[i-1]+1, seq[i]-1);
int now = 1;
int l = 1, r = node;
for(int i = 1, j = siz; i <= node; i += siz, j += siz){
while(now <= m && q[now].l >= i && q[now].l <= j){
while(r > q[now].r) del(r--);
while(l < q[now].l) del(l++);
ans[q[now].id] = sum;
while(l > i) add(--l); ++now;
} while(r < node) add(++r); while(l <= j) del(l++);
}
ll tmp = 1ll * mxlen * (mxlen+1) / 2;
for(int i = 1; i <= m; i++){
ll v = tmp - ans[i];
ll g = gcd(tmp, v);
cout << v / g << "/" << tmp / g << '\n';
} return 0;
}
原題過於困難咕了,有一道類似的 SP11414 COT3 - Combat on a tree
博弈論兩個基本定理
一個遊戲的 函數等於各個遊戲的函數的異或和
如果爲0,那麼先手必敗
看看本題
表示 x 的子樹的SG函數
表示 x 的子樹的所有後繼遊戲局面的集合
有
令
如果當前可以選,那麼在 中插入
然後考慮在當前的子樹中選一個點刪除,那麼這個的子樹一定是中的一個值
發現我們需要把合併到
發現需要合併,求,用解決
關於怎麼得到答案
考慮枚舉 u,然後就是刪掉 u 到根的鏈後剩下的聯通塊的異或起來,如果爲0說明後手必敗便可以最爲答案,剩下的聯通塊的異或可以時順便求出
#include<bits/stdc++.h>
#define N 200050
#define M 20000050
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 cov[M], ls[M], rs[M], tg[M];
int n, col[N];
int first[N], nxt[N], to[N], tot;
int sg[N], rt[N], node;
void add(int x, int y){ nxt[++tot] = first[x], first[x] = tot, to[tot] = y;}
void insert(int &x, int v, int i){
if(!x) x = ++node;
if(i == -1){ cov[x] = 1; return;}
if(v & (1<<i)) insert(rs[x], v, i-1);
else insert(ls[x], v, i-1);
}
void pushxor(int &x, int val, int i){
if(i <= -1) return;
if(val & (1<<i)) swap(ls[x], rs[x]);
tg[x] ^= val;
}
void pushdown(int x, int i){
if(!tg[x] || !x) return;
pushxor(ls[x], tg[x], i-1);
pushxor(rs[x], tg[x], i-1);
tg[x] = 0;
}
int merge(int x, int y, int i){
if(!x || !y) return x + y;
if(i == -1){ cov[x] |= cov[y]; return x;}
pushdown(x, i); pushdown(y, i);
ls[x] = merge(ls[x], ls[y], i-1);
rs[x] = merge(rs[x], rs[y], i-1);
cov[x] = cov[ls[x]] && cov[rs[x]];
return x;
}
int mex(int x, int i){
if(!x || i == -1) return 0;
pushdown(x, i);
if(!cov[ls[x]]) return mex(ls[x], i-1);
else return (1 << i) + mex(rs[x], i-1);
}
void dfs(int u, int fa){
int SG = 0;
for(int i = first[u]; i; i = nxt[i]) if(to[i] ^ fa) dfs(to[i], u), SG ^= sg[to[i]];
if(!col[u]) insert(rt[u], SG, 17);
for(int i = first[u]; i; i = nxt[i]){
int t = to[i]; if(t == fa) continue;
pushxor(rt[t], SG ^ sg[t], 17);
rt[u] = merge(rt[u], rt[t], 17);
} sg[u] = mex(rt[u], 17);
}
vector<int> v;
void dfs2(int u, int fa, int SG){
for(int i = first[u]; i; i = nxt[i]) if(to[i] ^ fa) SG ^= sg[to[i]];
if(SG == 0 && !col[u]) v.push_back(u);
for(int i = first[u]; i; i = nxt[i]){
if(to[i] ^ fa) dfs2(to[i], u, SG ^ sg[to[i]]);
}
}
int main(){
n = read();
for(int i = 1; i <= n; i++) col[i] = read();
for(int i = 1; i < n; i++){ int x = read(), y = read(); add(x, y); add(y, x);}
dfs(1, 0); dfs2(1, 0, 0);
if(v.size()){
sort(v.begin(), v.end());
for(int i = 0; i < v.size(); i++) cout << v[i] << " ";
}
else cout << "-1"; return 0;
}