26.10.7qbxt集训day7

T1

image
考虑必须要有个循环节,且循环节长度为 \(\gcd(n,k)\)。
多重组合数即可。

点击查看代码
#include <iostream>
#include <cmath>
#include <vector>

using std::cin;
using std::cout;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;

int cnt[N];
int col[(int)1e7 + 10];
int b[N], x[N], y[N];
int inv[(int)1e7 + 10];
int fac[(int)1e7 + 10];
int ifac[(int)1e7 + 10];
std::vector<int> modi;

int gcd(int x, int y)
{
	if (!x || !y)
		return x | y;
	int xx = __builtin_ctz(x);
	int yy = __builtin_ctz(y);
	int tmp;
	int k = std::min(xx, yy);
	y >>= yy;
	while (x)
	{
		x >>= xx;
		tmp = y - x;
		xx = __builtin_ctz(tmp);
		y = std::min(x, y);
		x = abs(tmp);
	}
	return y << k;
}
void rd(int &x)
{
	x = 0;
	int f = 1;
	char c = getchar();
	while (!isdigit(c))
	{
		if (c == '-')
			f = -f;
		c = getchar();
	}
	while (isdigit(c))
	{
		x = (x << 3) + (x << 1) - '0' + c;
		c = getchar();
	}
	x *= f;
}
void solve()
{
	for (auto it : modi)
		col[it] = 0;
	modi.clear();
	int n, k, c, q;
	rd(n), rd(k), rd(c), rd(q);
	for (int i = 1; i <= c; ++i)
		rd(b[i]);
	bool fl = true;
	int len = gcd(n, k);
	for (int i = 1; i <= c; ++i)
		cnt[i] = 0;
	for (int i = 1; i <= q; ++i)
	{
		int x, y;
		rd(x), rd(y);
		if (col[x % len] && col[x % len] != y)
			fl = false;
		if (!col[x % len])
			cnt[y]++;
		col[x % len] = y;
		modi.push_back(x % len);
	}
	if (!fl)
	{
		cout << 0 << '\n';
		return;
	}
	int ans = 1;
	int all = len;
	for (int i = 1; i <= c; ++i)
	{
		if (b[i] % (k / len))
		{
			cout << 0 << '\n';
			return;
		}
		b[i] /= (k / len);
		if (b[i] < cnt[i])
		{
			cout << 0 << '\n';
			return;
		}
		b[i] -= cnt[i];
		all -= cnt[i];
		ans = 1ll * ans * ifac[b[i]] % mod;
	}
	cout << 1ll * ans * fac[all] % mod << '\n';
}

int main()
{
	freopen("donut.in", "r", stdin);
	freopen("donut.out", "w", stdout);
	inv[1] = 1;
	for (int i = 2; i <= 1e7; ++i)
		inv[i] = 1ll * (-mod / i + mod) * inv[mod % i] % mod;
	fac[0] = ifac[0] = 1;
	for (int i = 1; i <= 1e7; ++i)
	{
		fac[i] = 1ll * fac[i - 1] * i % mod;
		ifac[i] = 1ll * ifac[i - 1] * inv[i] % mod;
	}
	int t;
	cin >> t;
	while (t--)
		solve();
	return 0;
}

T2

image
注意到它让输出所有的长度,所以长度的总种类数并不多,直接暴力可过。

点击查看代码
#include <iostream>
#include <map>
#include <queue>
#include <vector>

using std::cin;
using std::cout;
const int N = 110;
typedef long long ll;

bool vis[N];
int k[N];
int e[N][5];
std::queue<int> q;
//std::vector<int> cl;
std::map<ll, ll> ans;
std::map<ll, ll> cnt[N];

template<typename T>
void rd(T &x)
{
	x = 0;
	int f = 1;
	char c = getchar();
	while (!isdigit(c))
	{
		if (c == '-')
			f = -f;
		c = getchar();
	}
	while (isdigit(c))
	{
		x = (x << 3) + (x << 1) - '0' + c;
		c = getchar();
	}
	x *= f;
}

int main()
{
	freopen("baguette.in", "r", stdin);
	freopen("baguette.out", "w", stdout);
	ll n;
	int m;
	rd(n), rd(m);
	for (int i = 1; i <= m; ++i)
	{
		rd(k[i]);
		for (int j = 1; j <= k[i]; ++j)
		{
			int s;
			rd(s);
			e[i][j] = s;
		}
	}
	if (n == 1)
	{
		cout << 1 << ' ' << 1 << '\n';
		return 0;
	}
	cnt[1][n] = 1;
	q.push(1);
	vis[1] = true;
	ans[n] = 1;
	ll a;
	int b;
	while (q.size())
	{
		int x = q.front();
		q.pop();
		vis[x] = false;
		for (auto it : cnt[x])
		{
			if (!it.second)
				continue;
			a = it.first / k[x];
			b = it.first % k[x];
			cnt[x][it.first] -= it.second;
			if (b)
				ans[a + 1] += it.second * b;
			if (a == 0)
				continue;
			if (k[x] - b)
				ans[a] += it.second * (k[x] - b);
			for (int i = 1; i <= b; ++i)
			{
				cnt[e[x][i]][a + 1] += it.second;
				if (!vis[e[x][i]])
				{
					vis[e[x][i]] = true;
					q.push(e[x][i]);
				}
			}
			if (a == 1)
				continue;
			for (int i = b + 1; i <= k[x]; ++i)
			{
				cnt[e[x][i]][a] += it.second;
				if (!vis[e[x][i]])
				{
					vis[e[x][i]] = true;
					q.push(e[x][i]);
				}
			}
		}
//		for (auto it : cnt[x])
//		{
//			if (!it.second)
//				cl.push_back(it.first);
//		}
//		for (auto it : cl)
//			cnt[x].erase(it);
//		cl.clear();
	}
	for (auto it : ans)
		cout << it.first << ' ' << it.second << '\n';
	return 0;
}

T3

image
势能分析。

点击查看代码
#include <iostream>
#include <vector>
#include <unordered_map>

using std::cin;
using std::cout;
const int N = 1e5 + 10;
typedef long long ll;
struct Node
{
	int mx;
	int mx2;
	int a;
	ll ans;
	int ap;
	friend Node operator+(const Node &l, const Node &r)
	{
		Node ret;
		ret.mx = std::max(l.mx, r.mx);
		ret.mx2 = std::max(l.mx2, r.mx2);
		ret.ans = l.ans + r.ans;
		return ret;
	}
} z[N << 2];

char opt[6];
int a[N];
int b[N];
int p[N];
int rep[N];
std::vector<int> modi;

#define root 1, n, 1
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1

void build(int l, int r, int rt)
{
	if (l == r)
	{
		z[rt].a = a[l];
		z[rt].ap = a[p[l]];
		z[rt].mx = a[l] - std::max(a[p[l]], 1);
		z[rt].mx2 = a[l] - a[p[l]];
		z[rt].ans = a[l] - a[l] % std::max(a[p[l]], 1);
		return;
	}
	int mid = (l + r) >> 1;
	build(lson);
	build(rson);
	z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
void modify(int l, int r, int rt, int nowl, int nowr, int k)
{
	if (k == 0)
	{
		if (z[rt].mx < 0)
			return;
	}
	else
	{
		if (z[rt].mx2 - k < 0)
			return;
	}
	if (l == r)
	{
		b[p[l]] = a[p[l]];
		modi.push_back(l);
		return;
	}
	int mid = (l + r) >> 1;
	if (nowl <= mid)
		modify(lson, nowl, nowr, k);
	if (nowr > mid)
		modify(rson, nowl, nowr, k);
}
void modicov(int l, int r, int rt, int p, int k, int opt)
{
	if (l == r)
	{
		if (opt == 1)
			z[rt].a = k;
		else
			z[rt].ap = k;
		z[rt].mx = z[rt].a - std::max(z[rt].ap, 1);
		z[rt].mx2 = z[rt].a - z[rt].ap;
		z[rt].ans = z[rt].a - z[rt].a % std::max(z[rt].ap, 1);
		return;
	}
	int mid = (l + r) >> 1;
	if (p <= mid)
		modicov(lson, p, k, opt);
	else
		modicov(rson, p, k, opt);
	z[rt] = z[rt << 1] + z[rt << 1 | 1];
}
void rd(int &x)
{
	x = 0;
	int f = 1;
	char c = getchar();
	while (!isdigit(c))
	{
		if (c == '-')
			f = -f;
		c = getchar();
	}
	while (isdigit(c))
	{
		x = (x << 3) + (x << 1) - '0' + c;
		c = getchar();
	}
	x *= f;
}

int main()
{
	freopen("jam.in", "r", stdin);
	freopen("jam.out", "w", stdout);
	int n, q;
	rd(n), rd(q);
	for (int i = 1; i <= n; ++i)
		rd(a[i]);
	for (int i = 1; i <= n; ++i)
		rd(p[i]), rep[p[i]] = i;
	build(root);
	for (int i = 1; i <= q; ++i)
	{
		cin >> opt;
		if (opt[0] == 'S')
		{
			int u, v;
			rd(u), rd(v);
			std::swap(p[u], p[v]);
			modicov(root, u, a[p[u]], 2);
			modicov(root, v, a[p[v]], 2);
			rep[p[u]] = u, rep[p[v]] = v;
		}
		else if (opt[0] == 'M')
		{
			int l, r, x;
			rd(l), rd(r), rd(x);
			modify(root, l, r, x);
			for (auto it : modi)
			{
				a[it] %= std::max(b[p[it]] + x, 1);
				modicov(root, rep[it], a[it], 2);
				modicov(root, it, a[it], 1);
			}
			modi.clear();
		}
		cout << z[1].ans << '\n';
	}
	return 0;
}

T4

image
没听懂。

点击查看std
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N = 2005;
const int M = 1005;
const ll NEG = -(1LL << 60);

int n, tot, num, now;
int fa[N], w[N], son[N][2], deg[N];
int le[N], ri[N], id[N], leaf[M];
int dis[N][M], ord[N][M], que[M];
ll f[N][M];

bool inside(int x, int p) {
    return le[x] <= p && p <= ri[x];
}

void dfs(int x) {
    le[x] = num + 1;
    if (!deg[x]) {
        id[x] = ++num;
        leaf[num] = x;
    } else {
        dfs(son[x][0]);
        dfs(son[x][1]);
    }
    ri[x] = num;
}

void getdis(int x, int pre, int p, int d) {
    dis[x][p] = d;
    if (fa[x] && fa[x] != pre)
        getdis(fa[x], x, p, d + w[x]);
    for (int j = 0; j < deg[x]; ++j) {
        int y = son[x][j];
        if (y != pre) getdis(y, x, p, d + w[y]);
    }
}

bool cmp(int x, int y) {
    if (dis[now][x] != dis[now][y])
        return dis[now][x] < dis[now][y];
    return x < y;
}

// 将“x 的最近叶子”转换成“父亲的最近叶子”。
// 仅外部叶子的状态需要改变,内部叶子的状态保持不变。
void cross(int x) {
    int l = 0, r = 0, j = 1;
    for (int i = 1; i <= n; ++i) {
        int p = ord[x][i];
        if (inside(x, p)) continue;
        while (j <= n && dis[x][ord[x][j]] <= dis[x][p]) {
            int q = ord[x][j++];
            if (!inside(x, q) || f[x][q] == NEG) continue;
            while (l < r && f[x][que[r - 1]] <= f[x][q]) --r;
            que[r++] = q;
        }
        while (l < r && dis[x][que[l]] < dis[x][p] - 2 * w[x]) ++l;
        if (l < r) f[x][p] = max(f[x][p], f[x][que[l]]);
    }
}

// 判断是否存在叶集,使 a * D - b * W > 0。
bool check(int a, int b) {
    for (int x = tot; x >= 1; --x) {
        if (!deg[x]) {
            fill(f[x] + 1, f[x] + n + 1, NEG);
            f[x][id[x]] = 0;
        } else {
            int l = son[x][0], r = son[x][1];
            ll cl = (ll)b * w[l], cr = (ll)b * w[r];
            for (int p = 1; p <= n; ++p) {
                ll v = NEG;
                if (!inside(r, p)) v = max(v, f[l][p] - cl);
                if (!inside(l, p)) v = max(v, f[r][p] - cr);
                if (f[l][p] != NEG && f[r][p] != NEG) {
                    ll z = f[l][p] + f[r][p] - cl - cr;
                    // x 是所有被选叶子的 LCA:没有父边,不是三度点。
                    if (inside(x, p) && z > 0) return true;
                    // 父边也使用时,x 是三度点,增加最近叶子距离。
                    v = max(v, z + (ll)a * dis[x][p]);
                }
                f[x][p] = v;
            }
        }
        if (x != 1) cross(x);
    }
    return false;
}

int main() {
    freopen("biscuit.in", "r", stdin);
    freopen("biscuit.out", "w", stdout);
    scanf("%d", &n);
    tot = 2 * n - 1;
    for (int x = 2; x <= tot; ++x) {
        scanf("%d%d", &fa[x], &w[x]);
        son[fa[x]][deg[fa[x]]++] = x;
    }
    dfs(1);
    for (int p = 1; p <= n; ++p)
        getdis(leaf[p], 0, p, 0);
    for (int x = 1; x <= tot; ++x) {
        now = x;
        for (int p = 1; p <= n; ++p) ord[x][p] = p;
        sort(ord[x] + 1, ord[x] + n + 1, cmp);
    }

    // 三位小数相邻答案的分界为 (2 * mid + 1) / 2000。
    // 恰好等于分界时应向上舍入,所以判断必须使用严格大于零。
    int l = 500, r = 1000;
    while (l < r) {
        int mid = (l + r) >> 1;
        int p = 2 * mid + 1;
        if (check(p, 2000 - p)) r = mid;
        else l = mid + 1;
    }
    printf("%d.%03d\n", l / 1000, l % 1000);
    return 0;
}


题解:https://files.cnblogs.com/files/blogs/832279/dessert_solutions(1).zip?t=1791353940&download=true

posted @ 2026-10-07 14:19  SigmaToT  阅读(11)  评论(0)    收藏  举报