0522模擬賽 A. 求和 B.農民(farmer) C.仙人掌

A. 求和

在這裏插入圖片描述在這裏插入圖片描述
在這裏插入圖片描述在這裏插入圖片描述
求模s直接順着遞推。
在這裏插入圖片描述
求模2的次冪用倒推。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000105;
int n, m, K;
inline int qpow(int a, int b, int c) {
	int re = 1; a %= c;
	for(; b; b>>=1, a=1ll*a*a%c)if(b&1) re=1ll*re*a%c;
	return re;
}
int C[MAXN], F[MAXN];
void exgcd(int a, int b, int &x, int &y) {
	if(!b) { x = 1, y = 0; return; }
	exgcd(b, a%b, y, x); y -= x*(a/b);
}
int Inv(int N, int MOD) {
	int x, y; exgcd(N, MOD, x, y);
	x = (x % MOD + MOD) % MOD; return x;
}
int MOD, P;
struct node {
	int x, y;
	node(int x = 1, int y = 0):x(x), y(y){}
	inline node operator *(const node &o)const {
		return node(1ll*x*o.x%MOD, y+o.y);
	}
};
node get(int N) {
	node re;
	while(N % P == 0) N /= P, ++re.y;
	re.x = N; return re;
}
node inv(int N) {
	node re;
	while(N % P == 0) N /= P, --re.y;
	re.x = Inv(N, MOD); return re;
}
int cal(int p, int pk) {
	memset(C, 0, sizeof C);
	memset(F, 0, sizeof F);
	MOD = pk, P = p;
	int N = m+2; node now;
	for(int i = 1; i <= N && i <= n+2; ++i) {
		now = now * get(n+2-i+1) * inv(i);
		C[i] = 1ll * now.x * qpow(p, now.y, pk) % pk;
	}
	int inv2 = Inv(2, pk), re;
	re = F[0] = 1ll * C[1] * inv2 % pk;
	for(int i = 1; i <= m; ++i) {
		F[i] = 1ll * (C[i+1] - F[i-1]) % pk * inv2 % pk;
		if(!(i&1)) re = (re + F[i]) % pk;
	}
	return (re + pk) % pk;
}
int cnt, p[15], num[15], val[15];
int solve1(const int mod) { //s
	int tmp = mod;
	for(int i = 2; 1ll*i*i <= tmp; ++i)
		if(tmp % i == 0) {
			p[++cnt] = i; num[cnt] = 1;
			while(tmp % i == 0) tmp /= i, num[cnt] *= i;
		}
	if(tmp > 1) p[++cnt] = tmp, num[cnt] = tmp;
	int M = 1, re = 0;
	for(int i = 1; i <= cnt; ++i) val[i] = cal(p[i], num[i]), M *= num[i];
	for(int i = 1; i <= cnt; ++i) re = (re + 1ll * Inv(M/num[i], num[i]) * val[i] % mod * (M/num[i]) % mod) % mod;
	return re;
}
int solve2(int pk) {
	memset(C, 0, sizeof C);
	memset(F, 0, sizeof F);
	MOD = pk, P = 2;
	int N = m+2+30; node now;
	for(int i = 1; i <= N && i <= n+2; ++i) {
		now = now * get(n+2-i+1) * inv(i);
		C[i] = 1ll * now.x * qpow(2, now.y, pk) % pk;
	}
	F[m] = 0; int cf = 1;
	for(int i = m+2; cf; ++i) {
		F[m] = (F[m] + 1ll * cf * C[i]) % pk;
		cf = (-2ll) * cf % pk;
	}
	int re = F[m];
	for(int i = m-1; i >= 0; --i) {
		F[i] = (C[i+2] - 2ll*F[i+1]) % pk;
		if(!(i&1)) re = (re + F[i]) % pk;
	}
	return (re + pk) % pk;
}
int main () {
	scanf("%d%d%d", &n, &m, &K); if(n&1) --n; if(m&1) --m;
	int m1 = K, m2 = 1, t = 0;
	while(!(m1&1)) m1>>=1, m2<<=1, ++t;
	
	int v1 = solve1(m1), v2 = solve2(m2);
	int ans = (1ll*v1*Inv(m2, m1)*m2%K + 1ll*v2*Inv(m1, m2)*m1%K) % K;
	printf("%d\n", ans);
}

B.農民(farmer)

在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述
題解:對於一個權值爲xx的節點,它的左子樹內的節點有可能被走到僅當其權值小於xx,右子樹內的節點有可能被走到僅當其權值大於xx,那麼樹上每條邊相當於給這條邊以下的子樹加了一個大於或是小於的限制,詢問一個節點時,只要判斷這個節點的權值是否同時滿足到根路徑上所有邊的限制即可。我們可以用樹鏈剖分加線段樹維護這個限制,單點修改很好處理,子樹翻轉相當於取反子樹內所有限制的符號,線段樹同時維護一下翻轉後的限制,打標記維護即可。時間複雜度O(n+mlog2n)O(n+m \log^2⁡n)

#include <bits/stdc++.h>
using namespace std;
inline void read(int &x) {
	char ch; int flg=1; while(!isdigit(ch=getchar()))if(ch=='-')flg=-flg;
	for(x=ch-'0';isdigit(ch=getchar());x=x*10+ch-'0'); x*=flg;
}
const int MAXN = 100005;
const int inf = 1e9 + 5;
int n, m, rt, fa[MAXN], a[MAXN], ch[MAXN][2], top[MAXN], son[MAXN], sz[MAXN], dfn[MAXN], seq[MAXN], tmr;
void dfs1(int u) {
	sz[u] = 1;
	for(int i = 0, v; i < 2; ++i) if((v=ch[u][i])) dfs1(v), sz[u] += sz[v];
	son[u] = sz[ch[u][1]] > sz[ch[u][0]];
}
void dfs2(int u, int tp) {
	top[u] = tp; seq[dfn[u] = ++tmr] = u;
	if(ch[u][son[u]]) dfs2(ch[u][son[u]], tp);
	if(ch[u][son[u]^1]) dfs2(ch[u][son[u]^1], ch[u][son[u]^1]);
}
struct node {
	int a, b, c, d;
	node(int a=0, int b=inf, int c=0, int d=inf):a(a), b(b), c(c), d(d){}
	inline node rev() { return node(c, d, a, b); }
	inline node operator +(const node &o)const { return node(max(a, o.a), min(b, o.b), max(c, o.c), min(d, o.d)); }
}val[MAXN<<2]; bool rv[MAXN<<2];
void build(int i, int l, int r) {
	if(l == r) { val[i] = son[seq[l]] ? node(a[seq[l]], inf, 0, a[seq[l]]) : node(0, a[seq[l]], a[seq[l]], inf); return; }
	int mid = (l + r) >> 1;
	build(i<<1, l, mid);
	build(i<<1|1, mid+1, r);
	val[i] = val[i<<1] + val[i<<1|1];
}
inline void reving(int i) { val[i] = val[i].rev(); rv[i] ^= 1; }
inline void pd(int i) { if(rv[i]) rv[i]=0, reving(i<<1), reving(i<<1|1); }
void upd(int i, int l, int r, int x) {
	if(l == r) { val[i] = (son[seq[l]]^rv[i]) ? node(a[seq[l]], inf, 0, a[seq[l]]) : node(0, a[seq[l]], a[seq[l]], inf); return; }
	pd(i); int mid = (l + r) >> 1;
	x <= mid ? upd(i<<1, l, mid, x) : upd(i<<1|1, mid+1, r, x);
	val[i] = val[i<<1] + val[i<<1|1];
}
void mdf(int i, int l, int r, int x, int y) {
	if(x <= l && r <= y) { reving(i); return; }
	int mid = (l + r) >> 1; pd(i);
	if(x <= mid) mdf(i<<1, l, mid, x, y);
	if(y > mid) mdf(i<<1|1, mid+1, r, x, y);
	val[i] = val[i<<1] + val[i<<1|1];
}
node qry(int i, int l, int r, int x, int y) {
	if(x <= l && r <= y) return val[i];
	int mid = (l + r) >> 1; node re; pd(i);
	if(x <= mid) re = re + qry(i<<1, l, mid, x, y);
	if(y > mid) re = re + qry(i<<1|1, mid+1, r, x, y);
	return re;
}
bool chk(int x, int v) {
	node re;
	while(x) {
		if(x != top[x]) re = re + qry(1, 1, n, dfn[top[x]], dfn[fa[x]]);
		if((x = fa[top[x]])) re = re + qry(1, 1, n, dfn[x], dfn[x]).rev();
	}
	return re.a < v && v < re.b;
}
int main() {
	read(n), read(m);
	for(int i = 1; i <= n; ++i) {
		read(a[i]), read(ch[i][0]), read(ch[i][1]);
		if(ch[i][0]) fa[ch[i][0]] = i;
		if(ch[i][1]) fa[ch[i][1]] = i;
	}
	for(rt = 1; fa[rt]; ++rt);
	dfs1(rt); dfs2(rt, rt); build(1, 1, n);
	int op, x;
	while(m--) {
		read(op), read(x);
		if(op == 1)	read(a[x]), upd(1, 1, n, dfn[x]);
		else if(op == 2) mdf(1, 1, n, dfn[x], dfn[x]+sz[x]-1);
		else puts(chk(x, a[x]) ? "YES" : "NO");
	}
}

C.仙人掌

在這裏插入圖片描述在這裏插入圖片描述

題解:f(i,j)f(i,j)表示圓方樹上i的子樹內的點的生成子圖內的邊完成了定向,i的出度爲j的方案數,由於度數總和是O(n)O(n)的,所以狀態數總和不會超過O(n)O(n)。轉移分兩類,一種是整個環的轉移(即方點),枚舉環上一條邊的方向轉移即可;另一種是合併若干個環或橋邊的轉移(即圓點),根據環最上方兩條邊的方向或是橋邊的方向,是一個類似揹包的DPDP,可以直接用分治NTTNTT優化。時間複雜度O(nlog2n)O(n log^2⁡n)

#include <bits/stdc++.h>
using namespace std;
char cb[1<<15], *cs=cb, *ct=cb;
#define getc() (cs==ct && (ct = (cs=cb) + fread(cb,1,1<<15,stdin), cs==ct) ? 0 : *cs++)
inline void read(int &x) {
	char ch; int flg=1; while(!isdigit(ch=getc()))if(ch=='-')flg=-flg;
	for(x=ch-'0';isdigit(ch=getc());x=x*10+ch-'0'); x*=flg;
}
const int MAXN = 530000;
const int mod = 998244353, G = 3;
int n, m, a[MAXN], du[MAXN], tot;
int Fir[MAXN], To[MAXN], Nxt[MAXN], Cnt = 1;
int fir[MAXN], to[MAXN], nxt[MAXN], cnt;
inline void link(int u, int v) { to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; }
inline void Add(int u, int v) { To[++Cnt] = v; Nxt[Cnt] = Fir[u]; Fir[u] = Cnt; }
int dfn[MAXN], low[MAXN], tmr, stk[MAXN], indx;
void tarjan(int u, int ff) {
	dfn[u] = low[u] = ++tmr; stk[++indx] = u;
	for(int i = Fir[u], v; i; i = Nxt[i])
		if(i^ff^1) {
			if(!dfn[v=To[i]]) {
				tarjan(v, i), low[u] = min(low[u], low[v]);
				if(low[v] == dfn[u]) {
					link(u, ++tot);
					do link(tot, stk[indx]);
					while(stk[indx--] != v);
				}
				else if(low[v] > dfn[u]) --indx, link(u, v);
			}
			else low[u] = min(low[u], dfn[v]);
		}
}
int f[MAXN][3];
#define pb push_back
int cur; vector<int>vec[MAXN];
inline void add(int &x, int y) { x = x + y >= mod ? x + y - mod : x + y; }
inline int pls(int x, int y) { return x + y >= mod ? x + y - mod : x + y; }
int gen[MAXN], W, rev[MAXN], X[MAXN], Y[MAXN];
inline int qpow(int a, int b) {
	int re = 1; for(; b; b>>=1, a=1ll*a*a%mod)if(b&1) re=1ll*re*a%mod;return re;
}
void NTT(int *arr, int len, int flg) {
	for(int i = 0; i < len; ++i)if(rev[i]<i)swap(arr[i], arr[rev[i]]);
	for(int i = 2, v, B = W>>1; i <= len; i<<=1, B>>=1) //iB = W
		for(int j = 0; j < len; j+=i)
			for(int k = j, x = 0; k < j+i/2; ++k, x+=B)
				arr[k+i/2] = pls(arr[k], mod-(v = 1ll * arr[k+i/2] * gen[flg==1?x:W-x] % mod)), add(arr[k], v);
	if(flg == -1) {
		int iv = qpow(len, mod-2);
		for(int i = 0; i < len; ++i) arr[i] = 1ll * arr[i] * iv % mod;
	}
}
vector<int> cal(int l, int r) {
	if(l == r) return vec[l];
	int mid = (l + r) >> 1;
	vector<int>L = cal(l, mid), R = cal(mid+1, r);
	int len = 1, lb = L.size(), rb = R.size();
	while(len <= lb+rb-2) len<<=1;
	for(int i = 1; i < len; ++i)rev[i] = (rev[i>>1]>>1)|((i&1)*(len>>1));
	for(int i = 0; i < lb; ++i) X[i] = L[i];
	for(int i = lb; i < len; ++i) X[i] = 0;
	for(int i = 0; i < rb; ++i) Y[i] = R[i];
	for(int i = rb; i < len; ++i) Y[i] = 0;
	NTT(X, len, 1), NTT(Y, len, 1);
	for(int i = 0; i < len; ++i) X[i] = 1ll * X[i] * Y[i] % mod;
	NTT(X, len, -1);
	vector<int>re; re.resize(lb+rb-1);
	for(int i = 0, siz = re.size(); i < siz; ++i) re[i] = X[i];
	return re;
}
void getans(int *F, int A) {
	vector<int> re = cal(1, cur);
	re.resize(A+1);
	for(int i = 1; i <= A; ++i) add(re[i], re[i-1]);
	F[0] = re[A]; if(A > 0) F[1] = re[A-1]; if(A > 1) F[2] = re[A-2];
}
void dfs(int u) {
	for(int i = fir[u]; i; i = nxt[i]) dfs(to[i]);
	if(u <= n) {
		if(u == 5) {
			++u;--u;
		}
		if(!fir[u]) f[u][0] = 1, f[u][1] = a[u]>0, f[u][2] = a[u]>1;
		else {
			cur = 0;
			for(int i = fir[u], v; i; i = nxt[i]) {
				vec[++cur].clear();
				if((v=to[i]) > n) vec[cur].pb(f[v][2]);
				vec[cur].pb(f[v][1]), vec[cur].pb(f[v][0]);
			}
			getans(f[u], a[u]);
		}
	}
	else {
		for(int o = 0; o < 2; ++o) {
			int now[2] = { 0, 0 }, pre[2]; now[o] = 1;
			for(int i = fir[u], v; i; i = nxt[i]) {
				swap(now, pre); v = to[i];
				now[0] = (1ll * pre[0] * f[v][1] % mod + 1ll * pre[1] * f[v][2]) % mod;
				now[1] = (1ll * pre[0] * f[v][0] % mod + 1ll * pre[1] * f[v][1]) % mod;
			}
			(f[u][o] += now[1]) %= mod;
			(f[u][o+1] += now[0]) %= mod;
		}
	}
}
void pre(int N) {
	W = 1; while(W <= N) W<<=1;
	gen[0] = 1, gen[1] = qpow(G, (mod-1)/W);
	for(int i = 2; i <= W; ++i) gen[i] = 1ll*gen[i-1]*gen[1] % mod;
}
int main() {
	read(n), read(m); pre(2*(n-1));
	for(int i = 1, u, v; i <= m; ++i) read(u), read(v), Add(u, v), Add(v, u), ++du[u], ++du[v];
	for(int i = 1; i <= n; ++i) read(a[i]), a[i] = min(a[i], du[i]);
	tot = n; tarjan(1, 0); dfs(1);
	printf("%d\n", f[1][0]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章