“红旗杯”第十五届东北地区大学生程序设计竞赛

​ 东北地区赛,也是\(2021CCPC\)网络预选赛压力赛的题,感觉再不写就永远的咕咕咕了......

https://codeforces.com/gym/103145 传送门

A

题目大意

​ 给一个\(n*n\)的矩阵填充\([1,n^2]\)的数,定义\(a_i\)为第i行最小值,\(S=\{a_1,a_2...a_n\}\cap\{1,2...n\}\)。求\(\sum\vert S\vert(mod998244353)\)

题目分析

​ 考虑如何选择,假如在某行选择数字\(i\),那么\(i\)可从\(n\)行中的任意一行的\(n\)个任意列选择,假定\(i\)是对集合\(S\)有贡献的数字,那么对于选定的行就要保证其余\(n-1\)个元素大于\(i\),那么选择方式就为\(C_{n^2-i}^{n-1}\),此外这\(n-1\)个元素可以随便排列,那么方式为(n-1)!,考虑此行后,剩余的\(n^2-n\)个元素可以自行排列。又已知对于集合\(S\)有贡献的只有数字\(1~n\),那么对于上述式子求和即可,答案为\(\sum_{i=1} n*n!*(n^2-n)!*C_{n^2-i}^{n-1}\)

​ 顺带一提,这玩意的常数大的离谱......\(hdu\)上正常模拟并不能过去,打表或者分段打表可以加速这一过程,关于分段打表,可以看 https://oi-wiki.org/math/dictionary/。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <string.h>
#include <cmath>
#include <deque>
#include <vector>
#include <map>
#define Lint long long
#define pi acos(-1.0)
using namespace std;
const Lint mod = 998244353;
int T, n;
Lint f[5005 * 5005], inv[5005 * 5005];
Lint pw(Lint x, Lint y)
{
    Lint ans = 1;
    while (y)
    {
        if (y & 1)
        {
            ans = ans * x;
            ans %= mod;
        }
        y >>= 1;
        x = (x * x) % mod;
    }
    return ans;
}
Lint C(Lint x, Lint y)
{
    if (!x)
        return 1;
    if (!y)
        return 1;
    return f[x] * inv[y] % mod * inv[x - y] % mod;
}
int main()
{
    f[0] = 1;
    for (int i = 1; i <= 5000 * 5000; ++i)
        f[i] = f[i - 1] * (i % mod), f[i] %= mod;
    inv[5000 * 5000] = pw(f[5000 * 5000], mod - 2);
    for (int i = 5000 * 5000 - 1; i >= 0; --i)
    {
        inv[i] = inv[i + 1] * (i + 1) % mod;
        inv[i] %= mod;
    }
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        Lint ans = 0;
        for (int j = 1; j <= n; ++j)
        {
            ans += (C(n * n - j, n - 1) % mod);
            ans %= mod;
        }
        ans = ans * n % mod * f[n] % mod * f[n * n - n] % mod;
        printf("%lld\n", ans);
    }
    return 0;
}

C

题目大意

​ 给定一颗树,可删除任意点,要求删除后的树不存在单独节点的情况,求方案数。

题目分析

​ 树形\(dp\),考虑\(dp[x][0]\)为删除此点的方案数,\(dp[x][1]\)为保留此点并保留以此点为根节点至少有一个叶子节点的方案数,\(dp[x][2]\)为保留此点并删除所有叶子节点的方案数。

​ 对于删除\(x\)\(x\)的儿子\(v\)可以选择删除或保留并保留至少一个儿子,不可以保留并删除所有儿子,因为这样\(v\)就会独立。

​ 对于保留\(x\)并删除所有儿子:显然只有删除儿子\(v\)一种选择

​ 对于保留\(x\)并保留至少一个儿子:儿子的所有形态可进行一个累乘,但要减去保留\(x\)删除所有儿子的方法。

for (auto u : v[x])
	{
		if (u == fa)
			continue;
		dp[x][0] = dp[x][0] % mod * (dp[u][0] + dp[u][1]) % mod;
		dp[x][2] = dp[x][2] % mod * dp[u][0] % mod;
		dp[x][1] = dp[x][1] % mod * (dp[u][0] + dp[u][1] + dp[u][2]) % mod;
	}
	dp[x][1] = (dp[x][1] - dp[x][2] + mod) % mod;

​ 取模操作时似乎使用自加/自乘运算会有奇怪的锅......所以还是展开写叭

​ 对于边界问题,对于整个树的叶子节点,删去本身和保留此点删除所有叶子节点是合法的,而保留此点与至少一个叶子节点是非法的(不会有额外的叶子节点)。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <string.h>
#include <cmath>
#include <deque>
#include <vector>
#include <map>
#define Lint long long
#define pi acos(-1.0)
using namespace std;
int n;
const int N = 1e5 + 10;
const Lint mod = 998244353;
vector<int> v[N];
Lint dp[N][3];
void dfs(int x, int fa)
{
	int flag = 0;
	for (auto u : v[x])
	{
		if (u == fa)
			continue;
		flag++;
		dfs(u, x);
	}
	if (flag == 0)
	{
		dp[x][0] = 1;
		dp[x][1] = 0;
		dp[x][2] = 1;
		return;
	}
	dp[x][0] = 1;
	dp[x][1] = 1;
	dp[x][2] = 1;
	for (auto u : v[x])
	{
		if (u == fa)
			continue;
		dp[x][0] = dp[x][0] % mod * (dp[u][0] + dp[u][1]) % mod;
		dp[x][2] = dp[x][2] % mod * dp[u][0] % mod;
		dp[x][1] = dp[x][1] % mod * (dp[u][0] + dp[u][1] + dp[u][2]) % mod;
	}
	dp[x][1] = (dp[x][1] - dp[x][2] + mod) % mod;
}
int main()
{
	int x, y, T;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d", &n);
		for (int i = 1; i <= n; ++i)
		{
			v[i].clear();
		}
		for (int i = 1; i < n; ++i)
		{
			scanf("%d%d", &x, &y);
			v[x].push_back(y);
			v[y].push_back(x);
		}
		dfs(1, -1);
		printf("%lld\n", (dp[1][0] + dp[1][1]) % mod);
	}
 
	return 0;
}

D

题目大意

​ 给定一个初始序列,有两个操作:

​ 1.区间\([L,R]\)每个元素\(a_i\)增加\(lowbit(a_i)\)

​ 2.区间\([L,R]\)元素和(取模\(998244353\))

题目分析

​ 乍一看对于元素增加\(lowbit\)是很难拓展的一个操作,但我们先看\(lowbit\)操作的本质:取出一个数二进制下最小位的\(1\)及其后续。那么对于\(2\)的幂指数\(x\)\(lowbit(x)=x\)。对于任意数\(n\)显然\(log_2n\)次的\(lowbit\)操作可以将其变成\(2\)的幂指数。

​ 假若某一区间所有数都为\(2\)的幂指数,那么对于此区间的操作就为区间元素乘\(2\),否则我们递归到叶子节点进行暴力修改。

​ 需要额外注意的是,由于我们的操作要进行取模,且题目并未保证元素在\(lowbit\)操作后变为\(2\)的幂指数时小于模数,所以对于叶子节点,我们应额外开变量记载其原本值,以免在取模后\(lowbit\)操作出锅导致无法变成\(2\)的幂指数。

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #include <string.h>
    #include <cmath>
    #include <deque>
    #include <vector>
    #include <map>
    #define Lint long long
    #define pi acos(-1.0)
    using namespace std;
    const int N = 1e5 + 10;
    const long long mod = 998244353;
    Lint f[N], b[N];
    int n;
    Lint lowbit(Lint x) { return x & -x; }
    struct Tree
    {
        struct node
        {
            int l, r, qwq, lazy;
            Lint sum, val, times;
        };
        node a[N * 4 + 10];
        void pushup(int p)
        {
            a[p].sum = (a[p << 1].sum + a[p << 1 | 1].sum) % mod;
            a[p].qwq = a[p << 1].qwq & a[p << 1 | 1].qwq;
        }
        void pushdown(int p)
        {
            if (a[p].lazy)
            {
                a[p << 1].lazy += a[p].lazy;
                a[p << 1 | 1].lazy += a[p].lazy;
                a[p << 1].sum *= (f[a[p].lazy]);
                a[p << 1].sum %= mod;
                a[p << 1 | 1].sum *= (f[a[p].lazy]);
                a[p << 1 | 1].sum %= mod;
                a[p].lazy = 0;
            }
        }
        void change(int p)
        {
            if (a[p].qwq)
            {
                a[p].sum *= 2, a[p].sum %= mod;
                a[p].lazy++;
                return;
            }
            if (a[p].l == a[p].r)
            {
                if (!a[p].val)
                {
                    a[p].sum += lowbit(a[p].sum);
                    if (a[p].sum == lowbit(a[p].sum))
                    {
                        a[p].qwq = 1, a[p].val = a[p].sum;
                    }
                }
                else{
                    a[p].times++;
                    a[p].sum=a[p].val%mod*f[a[p].times];
                    a[p].sum %= mod;
                }
                return;
            }
            int mid = (a[p].l + a[p].r) >> 1;
            pushdown(p);
            change(p << 1), change(p << 1 | 1);
            pushup(p);
        }
        void build(int p, int l, int r)
        {
            a[p].l = l, a[p].r = r, a[p].qwq = 0, a[p].lazy = 0, a[p].times = 0, a[p].val = 0;
            if (l == r)
            {
                a[p].sum = b[l];
                if (a[p].sum == lowbit(a[p].sum))
                {
                    a[p].val = a[p].sum;
                    a[p].qwq = 1;
                }
                return;
            }
            int mid = (l + r) >> 1;
            build(p << 1, l, mid), build(p << 1 | 1, mid + 1, r);
            pushup(p);
        }
        void add(int p, int L, int R)
        {
            int l = a[p].l, r = a[p].r;
            if (l >= L && r <= R)
            {
                change(p);
                return;
            }
            pushdown(p);
            int mid = (l + r) >> 1;
            if (mid >= L)
            {
                add(p << 1, L, R);
            }
            if (mid + 1 <= R)
            {
                add(p << 1 | 1, L, R);
            }
            pushup(p);
        }
        Lint qurry(int p, int L, int R)
        {
            int l = a[p].l, r = a[p].r;
            if (l >= L && r <= R)
            {
                return a[p].sum % mod;
            }
            pushdown(p);
            Lint ans = 0;
            int mid = (l + r) >> 1;
            if (mid >= L)
                ans += qurry(p << 1, L, R);

            if (mid + 1 <= R)
                ans += qurry(p << 1 | 1, L, R);
            return ans % mod;
        }
    } Tree;

    int main()
    {
        f[0] = 1;
        for (int i = 1; i <= 100000; ++i)
        {
            f[i] = f[i - 1] * 2;
            f[i] %= mod;
        }
        int T, x, y, op, m;
        scanf("%d", &T);
        while (T--)
        {
            scanf("%d", &n);
            for (int i = 1; i <= n; ++i)
            {
                scanf("%lld", &b[i]);
            }
            Tree.build(1, 1, n);
            scanf("%d", &m);
            while (m--)
            {
                scanf("%d%d%d", &op, &x, &y);
                if (op == 1)
                {
                    Tree.add(1, x, y);
                }
                else
                    printf("%lld\n", Tree.qurry(1, x, y));
            }
        }
        return 0;
    }

E

题目大意

​ 给定一个数\(k\),构造出数\(x\),使得\(k\)\(x\)的因子且\(x\)所有因子的子集元素和等于\(x\)

题目分析

​ 显然\(6p=p+2p+3p\)。看准数据范围构造即可。

I

签到题......

K

题目大意

​ 给定图,每条边上有权值,多组询问,每次询问给定一个\(x\),判断有多少组节点相连(相连的前提为边权大于等于\(x\))。

题目分析

​ 只考虑边权大于等于x的边实际上是一个删边的操作,但我们知道删除边在并查集中是很棘手的,相反,添加边是很容易的。那么就像星球大战一样,我们从大到小考虑每次询问,每次都添加一些边,若添加边为\((u,v)\),那么增加的组数为\(Size_u*Size_v\)

​ 此外,如果最小的数个询问没有增加新的边,那么答案需要继承较其更大的值。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <string.h>
#include <cmath>
#include <deque>
#include <vector>
#include <map>
#define Lint long long
#define pi acos(-1.0)
using namespace std;
typedef pair<int, int> P;
const int N = 1E5 + 10;
const int M = 2E5 + 10;
int T, n, m, q;
P b[M];
struct node
{
    int f[N];
    Lint Size[N];
    void build()
    {
        for (int i = 1; i <= n; ++i)
        {
            f[i] = i;
            Size[i] = 1;
        }
    }
    int Find(int x)
    {
        if (f[x] == x) return x;
        return f[x] = Find(f[x]);
    }
    void marge(int x, int y)
    {
        int le = Find(x), re = Find(y);
        f[le] = re;
        Size[re] += Size[le];
    }
} fa;
struct edge
{
    int x, y, z;
} e[M];
bool cmp(edge x, edge y)
{
    return x.z < y.z;
}
Lint c[M];
int main()
{
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d%d%d", &n, &m, &q);
        fa.build();
        Lint ans = 0;
        for (int i = 1; i <= m; ++i)
        {
            scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].z);
        }
        sort(e + 1, e + 1 + m, cmp);
        for (int i = 1; i <= q; ++i)
        {
            scanf("%d", &b[i].first);
            b[i].second = i;
        }
        sort(b + 1, b + 1 + q);
        int now = q; //最大
        for (int i = m; i >= 1; --i)
        {
            while (e[i].z < b[now].first)
            {
                c[b[now].second] = ans;
                now--;
            }
            if (fa.Find(e[i].x) != fa.Find(e[i].y))
            {
                ans += (Lint)fa.Size[fa.Find(e[i].x)] * fa.Size[fa.Find(e[i].y)];
                fa.marge(e[i].x, e[i].y);
            }
        }
        while(now){
            c[b[now].second]=ans;
            now--;
        }
        for (int i = 1; i <= q; ++i)
        {
            printf("%lld\n", c[i]);
        }
    }
    return 0;
}
posted @ 2021-09-06 20:56  wzyyy  阅读(501)  评论(0)    收藏  举报