2023.2.1 闲话

题目来自网络 .

\(V\) 为值域 .

SCOI2016 萌萌哒

首先考虑暴力就是两区间相等转写为对应位相等,然后并查集连边,若连通块数量为 \(c\) 则答案就是 \(9\cdot 10^{c-1}\),因为包含首位的连通块不能是 \(0\) .

在此基础考虑一个倍增,考虑建 \(\Theta(n\log n)\) 个点,每个区间可以拆成 \(\Theta(\log n)\) 段对应相等,并查集连边即可 .

然后对于每个区间对半分开下放到下一层,这样就可以得到最底层的答案,按上面方法算即可 .

时间复杂度 \(\Theta((n+m)\log n)\) .

Code
const int N = 1e5 + 123, L = __lg(N) + 2, P = 1e9 + 7;
inline int qpow(int a, int n)
{
	int ans = 1;
	while (n)
	{
		if (n & 1) ans = 1ll * ans * a % P;
		a = 1ll * a * a % P; n >>= 1;
	} return ans;
}
struct dsu
{
	int fa[N*L];
	dsu(){iota(fa, fa+N*L, 0);}
	int get(int x){return (x == fa[x]) ? x : fa[x] = get(fa[x]);}
	inline void merge(int u, int v){fa[get(u)] = get(v);}
}D;
int n, m, id[N][L];
int main()
{
	scanf("%d%d", &n, &m); int cc = 0, _ = __lg(n);
	for (int i=0; i<=_; i++)
		for (int j=1; j<=n; j++) id[j][i] = ++cc;
	for (int i=1,l1,r1,l2,r2; i<=m; i++)
	{
		scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
		int len = r1 - l1 + 1;
		for (int k=_; k>=0; k--)
			if (len >> k & 1)
			{
				D.merge(id[l1][k], id[l2][k]);
				l1 += (1 << k); l2 += (1 << k);
			}
	}
	for (int j=_; j>=1; j--)
		for (int i=1; i+(1<<j)-1<=n; i++)
		{
			int x = D.get(id[i][j]);
			if (x == id[i][j]) continue;
			x = (x - 1) % n + 1;
			D.merge(id[x][j-1], id[i][j-1]); D.merge(id[x+(1<<(j-1))][j-1], id[i+(1<<(j-1))][j-1]);
		}
	set<int> S;
	for (int i=1; i<=n; i++) S.insert((D.get(id[i][0]) - 1) % n + 1);
	printf("%lld\n", 9ll * qpow(10, S.size() - 1) % P);
	return 0;
}

CF359D Pair of Numbers

我怎么感觉我做过原题 .

每个点处理左右能到最长区间,ST 表维护区间 GCD 然后二分即可 .

时间复杂度 \(\Theta(n\log n\log V)\) .

Code
const int N = 3e5 + 123, L = __lg(N) + 2;
inline int gcd(int a, int b){return b ? gcd(b, a % b) : a;}
int n, a[N];
struct ST{/**/}T;
set<int> ans;
int len;
int main()
{
	scanf("%d", &n);
	for (int i=1; i<=n; i++) scanf("%d", a+i);
	T.reset(a+1, a+1+n);
	for (int i=1; i<=n; i++)
	{
		int L, R;
		int l = 1, r = i;
		while (l <= r)
		{
			int mid = (l + r) >> 1;
			if (!(T.query(mid, i) % a[i])){r = mid - 1; L = mid;}
			else l = mid + 1;
		}
		l = i; r = n;
		while (l <= r)
		{
			int mid = (l + r) >> 1;
			if (!(T.query(i, mid) % a[i])){l = mid + 1; R = mid;}
			else r = mid - 1;
		}
		if (R - L > len){len = R - L; ans.clear(); ans.insert(L);}
		else if (R - L == len) ans.insert(L);
	}
	printf("%lu %d\n", ans.size(), len);
	for (int x : ans) printf("%d ", x);
	puts("");
	return 0;
}

Alice

两个人,两堆石子,每次可以从任意一堆石子中取出若干个 .

每次操作取出的石子个数应满足为另一堆石子个数的因数,将石子取完者获胜。

给一个初始状态问谁必胜 .

注意到一奇一偶的情况只能变成俩奇数,于是对面再给变回一奇一偶, 直到有一个为 \(0\) 就可以赢了 .

于是一奇一偶必胜,两奇必败,两偶数的同时除以几个 \(2\) 即可,容易证明除因子不影响答案 .

Code
const int N = 1e5 + 7;
int n, m;
int gcd(int a, int b){return b ? gcd(b, a%b) : a;}
inline void solve()
{
	scanf("%d%d", &n, &m);
	if (!m){puts("A"); return ;}
	int _ = gcd(n, m); n /= _; m /= _;
	if ((n + m) & 1) puts("A");
	else puts("B");
}
int main()
{
	int T; scanf("%d", &T);
	while (T--) solve();
	return 0;
}

Common

根据 \(t_1,t_2,t_3\) 构造矩阵

\[a_{i,j}=t_1\cdot a_{i-1,j}+t_2\cdot a_{i,j-1}+t_3\cdot a_{i-1,j-1} \]

边界 \(a_{0,0}=1\) .

给序列 \(\{b_n\},\{c_n\}\),求

\[\sum_{i=1}^n\sum_{j=1}^na_{b_i+b_j,c_i+c_j} \]

答案对 \(998244353\) 取模 .

\(n\le 10^5\)\(b_i,c_i\le 2000\) .

注意到 \(a\) 矩阵是由递推形式给出 .

于是可以在 \(a\) 上操作,相当于 \(\Theta(V^2)\) 次操作一起做 .

具体的,对于每个 \(i\),在 \((-b_i,-c_i)\) 上加一,递推后在 \((b_i,c_i)\) 处统计答案即可 .

负下标可以平移解决 .

Code
const int M = 8888, N = 3919810, P = 998244353;
int n, t1, t2, t3, a[M][M], b[N], c[N];
int main()
{
	scanf("%d%d%d%d", &n, &t1, &t2, &t3);
	for (int i=1; i<=n; i++) scanf("%d", b+i);
	for (int i=1; i<=n; i++) scanf("%d", c+i), ++a[4000 - b[i]][4000 - c[i]];
	for (int i=-4000; i<=4000; i++)
		for (int j=-4000; j<=4000; j++)
		{
			(a[4000 + i][4000 + j] += 1ll * t1 * a[4000 + i-1][4000 + j] % P) %= P;
			(a[4000 + i][4000 + j] += 1ll * t2 * a[4000 + i][4000 + j-1] % P) %= P;
			(a[4000 + i][4000 + j] += 1ll * t3 * a[4000 + i-1][4000 + j-1] % P) %= P;
		}
	int ans = 0;
	for (int i=1; i<=n; i++) (ans += a[4000 + b[i]][4000 + c[i]]) %= P;
	printf("%d\n", ans);
	return 0;
}

建造游乐园

计数 \(n\) 个点的可以通过恰好一次添加一条不在图中的边或是删去一条图中的边使得图中存在一条欧拉回路简单图的个数,答案对 \(10^9+7\) 取模 .

\(2\le n\le 2000\) .

答案即为连通欧拉图数量乘 \(\dbinom n2\) .

一种做法是注意到每个连通块都是欧拉图的图就是每个节点度为偶数的图,数量也就是 \(2^{\binom{n-1}2}\) .

然后显然就有 EGF 的 exp 关系,从而得到答案就是

\[\left[\dfrac{z^n}{n!}\right]\ln\left(\sum_{k\ge 0}2^{\binom{k-1}2}\dfrac{z^n}{n!}\right) \]

即可 \(\Theta(n\log n)\),如果不想用 MTT 暴力写 \(\Theta(n^2)\) 也行 .

或者令 \(f_i\) 是答案,\(g_i\) 是每个节点度为偶数的图,根据上面讨论可以得到 \(g_n=2^{\binom{n-1}2}\) .

枚举有多少个点和 \(1\) 在同一连通块,可以得到

\[f_i=g_i-\sum_{j=1}^{i-1}\dbinom{i-1}{j-1}f_ig_{i-j} \]

半在线卷积 \(\Theta(\mathsf S(n))\),暴力 \(\Theta(n^2)\) .

以下是暴力:

Code
const int N = 2222, INF = 0x3f3f3f3f, P = 1e9+7;
int n;
ll C[N][N], f[N], g[N];
ll qpow(ll a, ll n)
{
	ll ans = 1;
	while (n)
	{
		if (n&1) ans = ans * a % P;
		a = a * a % P; n >>= 1;
	} return ans;
}
int main()
{
	C[0][0] = 1;
	for (int i=1; i<N; i++)
	{
		C[i][0] = 1;
		for (int j=1; j<=i; j++) C[i][j] = (C[i-1][j] + C[i-1][j-1]) % P;
	}
	scanf("%d", &n);
	for (int i=1; i<=n; i++) g[i] = qpow(2, C[i-1][2]);
	for (int i=1; i<=n; i++)
	{
		f[i] = g[i];
		for (int j=1; j<i; j++) f[i] = (f[i] - f[j] * g[i-j] % P * C[i-1][j-1] % P + P) % P;
	} printf("%lld\n", f[n] * C[n][2] % P);
	return 0;
}

WC2011 最大XOR和路径

结论是随便整一棵生成树算其中的环,然后随便找一条链和环跑最大异或和即可,证明可以感性理解一下 .

时间复杂度 \(\Theta((n+m)\log V)\) .

重题 CF845G .

Code
const int N = 55555;
int n, m;
ll ans[N];
vector<pair<int, ll>> g[N];
inline void addedge(int u, int v, ll w){g[u].emplace_back(make_pair(v, w));}
inline void ade(int u, int v, ll w){addedge(u, v, w); addedge(v, u, w);}
bool vis[N];
struct basis{/**/}B;
inline ll maxxor(const basis& _, const ll& z = 0)
{
	ll ans = z;
	for (int i= _.B-1; ~i; i--) chkmax(ans, ans ^ _[i]);
	return ans;
}
void dfs(int u, ll val)
{
	vis[u] = true; ans[u] = val;
	for (auto [v, w] : g[u])
		if (vis[v]) B.insert(ans[u] ^ ans[v] ^ w);
		else dfs(v, val ^ w);
}
int main()
{
	scanf("%d%d", &n, &m); ll w;
	for (int i=0, u, v; i<m; i++) scanf("%d%d%lld", &u, &v, &w), ade(u, v, w);
	dfs(1, 0);
	printf("%lld\n", maxxor(B, ans[n]));
	return 0;
}

CF819B Mister B and PR Shifts

考虑 Shift 一位产生的贡献即可 .

代码不太想写 .

COCI2014-2015#2 Norma

套路题,CDQ 分治或扫描线,\(\Theta(n\log n)\) .

代码略 .

CF600E Lomsat gelral

Code
const int N = 114514;
int n;
ll a[N], siz[N], son[N], buc[N], ans[N], now, nowans;
vector<int> g[N];
inline void addedge(int u, int v){g[u].emplace_back(v);}
inline void ade(int u, int v){addedge(u, v); addedge(v, u);}
inline void dfs0(int u, int fa)
{
	siz[u] = 1;
	for (int v : g[u])
	{
		if (v == fa) continue;
		dfs0(v, u);
		siz[u] += siz[v];
		if (!son[u] || (siz[v] > siz[son[u]])) son[u] = v;
	}
}
inline void ins(int x)
{
	++buc[x];
	if (buc[x] == now) nowans += x;
	if (buc[x] > now){now = buc[x]; nowans = x;}
}
inline void cls(int u, int fa)
{
	--buc[a[u]];
	for (int v : g[u])
		if (v != fa) cls(v, u);
}
inline void insd(int u, int fa)
{
	ins(a[u]);
	for (int v : g[u])
		if (v != fa) insd(v, u);
}
inline void dfs(int u, int fa, bool clr)
{
	for (int v : g[u])
		if ((v != fa) && (v != son[u])) dfs(v, u, true);
	if (son[u]) dfs(son[u], u, false);
	for (int v : g[u])
		if ((v != fa) && (v != son[u])) insd(v, u);
	ins(a[u]); ans[u] = nowans;
	if (clr){now = nowans = 0; cls(u, fa);}
}
int main()
{
	scanf("%d", &n);
	for (int i=1; i<=n; i++) scanf("%lld", a+i);
	for (int i=1, u, v; i<n; i++) scanf("%d%d", &u, &v), ade(u, v);
	dfs0(1, 0); dfs(1, 1, 0);
	for (int i=1; i<=n; i++) printf("%lld ", ans[i]);
	return 0;
}
posted @ 2023-02-01 11:14  Jijidawang  阅读(68)  评论(1编辑  收藏  举报
😅​