组合数问题
很明显最下面的中间最大
把它放进堆,然后每次取最大,将它左右上分别放进堆中就可以了
现在的难题转换为求大小
一个套路就取
l
o
g
log
log 乘法转加法
#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;
}
字符串
首先有个暴力的莫队做法,就是先建好
t
r
i
e
trie
trie,然后每次插入删除字符串可以看做
t
r
i
e
trie
trie树上的链加
复杂度
O
(
n
n
∗
l
e
n
)
O(n\sqrt n*len)
O(nn∗len)
注意到
∑
s
i
≤
3
e
5
\sum s_i\le3e5
∑si≤3e5,于是可以把所有字符串拼在一起然后莫队,中途需要用
s
e
t
set
set维护一下
插入一个点,就是查前驱后继,减去大区间的贡献,加上两边区间的贡献
复杂度
O
(
n
n
∗
l
o
g
n
)
O(n\sqrt n*logn)
O(nn∗logn) ,然后凉了
考虑用链表代替
s
e
t
set
set,应为链表不支持插入插入,支持删除,撤销,所以我们想到了一种回滚莫队的东西
实现如下----左端点相同按右端点从大到小排序,否则按左端点的块排序
然后按照块一块一块地处理
关于右端点的移动,每次从 n 往前挪,链表删除就是了
关于左端点的移动,每次撤回到块头,然后删除到
q
l
ql
ql
右端点每块要移 n 次,是
n
∗
n
n * \sqrt n
n∗n的,左端点每个询问最多移
n
\sqrt n
n次,也是
n
∗
n
n * \sqrt n
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
博弈论两个基本定理
S
G
(
x
)
=
m
e
x
(
S
)
SG(x)=mex(S)
SG(x)=mex(S)
一个游戏的
S
G
SG
SG函数等于各个游戏的
S
G
SG
SG函数的异或和
如果
S
G
SG
SG为0,那么先手必败
看看本题
S
G
(
x
)
SG(x)
SG(x) 表示 x 的子树的SG函数
g
[
x
]
g[x]
g[x] 表示 x 的子树的所有后继游戏局面的集合
有
S
G
(
x
)
=
m
e
x
(
g
[
x
]
)
SG(x) = mex(g[x])
SG(x)=mex(g[x])
令
s
u
m
[
x
]
=
S
G
(
v
1
)
⨂
S
G
(
v
2
)
.
.
.
⨂
S
G
(
v
k
)
sum[x]=SG(v_1)\bigotimes SG(v_2)...\bigotimes SG(v_k)
sum[x]=SG(v1)⨂SG(v2)...⨂SG(vk)
如果当前可以选,那么在
g
[
x
]
g[x]
g[x]中插入
s
u
m
[
x
]
sum[x]
sum[x]
然后考虑在当前
v
k
v_k
vk的子树中选一个点删除,那么这个
v
k
v_k
vk的子树一定是
g
[
v
k
]
g[v_k]
g[vk]中的一个值
发现我们需要把
g
[
v
k
]
[
j
]
⨂
s
u
m
[
x
]
⨂
S
G
(
v
k
)
g[v_k][j] \bigotimes sum[x]\bigotimes SG(v_k)
g[vk][j]⨂sum[x]⨂SG(vk)合并到
g
[
x
]
g[x]
g[x]
发现需要合并,求
m
e
x
mex
mex,用
0
/
1
t
r
i
e
0/1trie
0/1trie解决
关于怎么得到答案
考虑枚举 u,然后就是删掉 u 到根的链后剩下的联通块的
S
G
SG
SG异或起来,如果为0说明后手必败便可以最为答案,剩下的联通块的
S
G
SG
SG异或可以
d
f
s
dfs
dfs时顺便求出
#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;
}
本文探讨了组合数问题的高效解决策略,包括使用堆优化选取最大值,以及通过log运算简化复杂度。同时,深入解析了字符串处理的高级技巧,如Trie树和链表优化的莫队算法,用于快速插入和删除操作。
3268

被折叠的 条评论
为什么被折叠?



