省選模擬 19/09/13

組合數問題
很明顯最下面的中間最大
把它放進堆,然後每次取最大,將它左右上分別放進堆中就可以了
現在的難題轉換爲求大小
一個套路就取 loglog 乘法轉加法

#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;
}

字符串
首先有個暴力的莫隊做法,就是先建好 trietrie,然後每次插入刪除字符串可以看做trietrie樹上的鏈加
複雜度 O(nnlen)O(n\sqrt n*len)
注意到si3e5\sum s_i\le3e5,於是可以把所有字符串拼在一起然後莫隊,中途需要用setset維護一下
插入一個點,就是查前驅後繼,減去大區間的貢獻,加上兩邊區間的貢獻
複雜度O(nnlogn)O(n\sqrt n*logn) ,然後涼了
考慮用鏈表代替 setset,應爲鏈表不支持插入插入,支持刪除,撤銷,所以我們想到了一種回滾莫隊的東西
實現如下----左端點相同按右端點從大到小排序,否則按左端點的塊排序
然後按照塊一塊一塊地處理
關於右端點的移動,每次從 n 往前挪,鏈表刪除就是了
關於左端點的移動,每次撤回到塊頭,然後刪除到qlql
右端點每塊要移 n 次,是nnn * \sqrt n的,左端點每個詢問最多移n\sqrt n次,也是nnn * \sqrt 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
博弈論兩個基本定理
SG(x)=mex(S)SG(x)=mex(S)
一個遊戲的 SGSG函數等於各個遊戲的SGSG函數的異或和
如果SGSG爲0,那麼先手必敗
看看本題
SG(x)SG(x) 表示 x 的子樹的SG函數
g[x]g[x] 表示 x 的子樹的所有後繼遊戲局面的集合
SG(x)=mex(g[x])SG(x) = mex(g[x])
sum[x]=SG(v1)SG(v2)...SG(vk)sum[x]=SG(v_1)\bigotimes SG(v_2)...\bigotimes SG(v_k)
如果當前可以選,那麼在 g[x]g[x]中插入sum[x]sum[x]
然後考慮在當前vkv_k的子樹中選一個點刪除,那麼這個vkv_k的子樹一定是g[vk]g[v_k]中的一個值
發現我們需要把g[vk][j]sum[x]SG(vk)g[v_k][j] \bigotimes sum[x]\bigotimes SG(v_k)合併到g[x]g[x]
發現需要合併,求mexmex,用0/1trie0/1trie解決
關於怎麼得到答案
考慮枚舉 u,然後就是刪掉 u 到根的鏈後剩下的聯通塊的SGSG異或起來,如果爲0說明後手必敗便可以最爲答案,剩下的聯通塊的SGSG異或可以dfsdfs時順便求出

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章