8.26训练

8.26The 15th Chinese Northeast Collegiate Programming Contest

C(树形dp)

题意:随意删除树上任意结点,保证删除节点后,树上所有结点都与至少一个点相连,问删除节点的方案数

表示状态

f [ i ] [ 0 ] f[i][0] f[i][0]表示删除当前i结点

f [ i ] [ 1 ] f[i][1] f[i][1]表示删除所有子节点,需要爹

f [ i ] [ 2 ] f[i][2] f[i][2]表示有至少一个儿子

f [ u ] [ 0 ] = ∏ v ∈ s o n [ u ] ( f [ v ] [ 0 ] + f [ v ] [ 2 ] ) f[u][0] = \prod_{v∈son[u]}(f[v][0]+f[v][2]) f[u][0]=vson[u](f[v][0]+f[v][2])

f [ u ] [ 1 ] = ∏ v ∈ s o n [ u ] ( f [ v ] [ 0 ] ) f[u][1] = \prod_{v∈son[u]}(f[v][0]) f[u][1]=vson[u](f[v][0])

f [ u ] [ 2 ] f[u][2] f[u][2]采用全部减去 f [ v ] [ 1 ] f[v][1] f[v][1]

f [ u ] [ 2 ] = ∏ v ∈ s o n [ u ] ( f [ v ] [ 0 ] + f [ v ] [ 1 ] + f [ v ] [ 2 ] ) f[u][2] = \prod_{v∈son[u]}(f[v][0]+f[v][1]+f[v][2]) f[u][2]=vson[u](f[v][0]+f[v][1]+f[v][2])

f [ u ] [ 2 ] − = f [ u ] [ 1 ] f[u][2] -= f[u][1] f[u][2]=f[u][1]

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
 
using namespace std;
typedef long long ll;
const int MAXN = 2e5+50, mod = 998244353;
 
int T, n, id;
 
struct Edge{
    int to, next;
}E[MAXN];
 
ll head[MAXN], f[MAXN][3];
 
inline void addedge(int a, int b)
{
    E[id].to = b;
    E[id].next = head[a];
    head[a] = id++;
    return;
}
 
void dfs(int cur, int father)
{
    for(int i = head[cur]; i != -1;i = E[i].next)
    {
        int p = E[i].to;
        if(p == father) continue;
        dfs(p, cur);
        f[cur][0] = (f[p][0] + f[p][2]) % mod * f[cur][0] % mod;
        f[cur][1] = f[p][0] * f[cur][1] % mod;
        f[cur][2] = (f[p][0] + f[p][1] + f[p][2]) * f[cur][2] % mod;
    }
    f[cur][2] = (f[cur][2] - f[cur][1] + mod) % mod;
}
 
int main()
{
    cin >> T;
    while(T--)
    {
        id = 0;
        memset(head, -1, sizeof(head));
        cin >> n;
        for(int i = 1;i <= n;i++) f[i][0] = f[i][1] = f[i][2] = 1;
        for(int i = 0;i < n - 1;i++)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            addedge(u, v);
            addedge(v, u);
        }
        dfs(1, -1);
        printf("%lld\n", (f[1][0] + f[1][2]) % mod);
    }
    return 0;
}

E (构造)

题意:求p的倍数满足其一部分因数之和是他本身

6 p = p + 2 p + 3 p 6p=p+2p+3p 6p=p+2p+3p

#include <iostream>

using namespace std;

typedef long long ll;

int main()
{
    int T;
    scanf("%d", &T);
    while(T --) {
        ll p;
        scanf("%lld", &p);
        printf("%lld 3\n", (ll)6 * p);
        printf("%lld %lld %lld\n", p, (ll)2 * p, (ll)3 * p);
    }
    return 0;
}

I (签到)

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int N = 8;

int n;
int a[N] = {0, 7, 27, 41, 49, 63, 78, 108};

int main()
{
    int T;
    scanf("%d", &T);
    while(T --) {
        scanf("%d", &n);
        int tot = 0;
        for(int i = 1; i <= n; i ++) {
            int x;
            scanf("%d", &x);
            tot += a[x];
        }
        if(tot < 69) printf("%d\n", tot);
        else if(tot >= 69 && tot < 89) printf("%d\n", tot - 15);
        else if(tot >= 89 && tot < 120) printf("%d\n", tot - 30);
        else if(tot >= 120) printf("%d\n", tot - 50);
    }
    return 0;
}

M(字符串)

#include <cstdio>
#include <string>
#include <cstring>
#include <map>
using namespace std;
map<string,char> m;
map<string,char>::iterator it;
char x; string tmp="";
int main()
{
	m["iu"]='q';	m["q"]='q';
	m["ei"]='w';	m["w"]='w';
	m["e"]='e';
	m["uan"]='r';	m["r"]='r';
	m["ue"]='t';	m["t"]='t';
	m["un"]='y';	m["y"]='y';
	m["sh"]='u';	m["u"]='u';
	m["ch"]='i';	m["i"]='i';
	m["uo"]='o';	m["o"]='o';
	m["ie"]='p';	m["p"]='p';
	m["a"]='a';
	m["ong"]='s';	m["iong"]='s';	m["s"]='s';
	m["ai"]='d';	m["d"]='d';
	m["en"]='f';	m["f"]='f';	
	m["eng"]='g';	m["g"]='g';	
	m["ang"]='h';	m["h"]='h';	
	m["an"]='j';	m["j"]='j';	
	m["uai"]='k';	m["ing"]='k';	m["k"]='k';
	m["uang"]='l';	m["iang"]='l';	m["l"]='l';			
	m["ou"]='z';	m["z"]='z';	
	m["ia"]='x';	m["ua"]='x';	m["x"]='x';	
	m["ao"]='c';	m["c"]='c';	
	m["zh"]='v';	m["ui"]='v';	m["v"]='v';	
	m["in"]='b';	m["b"]='b';	
	m["iao"]='n';	m["n"]='n';	
	m["ian"]='m';	m["m"]='m';	
	x=getchar();
	while(x!=EOF)//sh
	{
		tmp="";
		while(x!=10&&x!=13&&x!=32&&x!=EOF)
			tmp+=x,x=getchar();
		//cout<<"##"<<tmp<<"# ";
		if(tmp.length()==1)
		{
			putchar(tmp[0]);
			putchar(tmp[0]);
		}
		else if(tmp.length()==2)
		{
			putchar(tmp[0]);
			putchar(tmp[1]);
		}
		else
		{
			it=m.find(tmp);
			if(it!=m.end())
			{
				putchar(tmp[0]);
				putchar(m[tmp]);
			}
			else
			{
				string mm="";
				mm+=tmp[0];
				mm+=tmp[1];
				it=m.find(mm);
				if(it!=m.end())
				{
					putchar(m[mm]);
					tmp.erase(0,2);
					putchar(m[tmp]);
				}
				else
				{
					putchar(tmp[0]);
					tmp.erase(0,1);
					putchar(m[tmp]);
				}
			}
		}
		while(x==10||x==13||x==32)
			putchar(x),x=getchar();
	}
	return 0;
}

A (组合计数)

首先, 1 , 2 , 3 , … , n 1,2,3,\dots,n 1,2,3,,n每个数字的位置可以随意调换而不影响 ∣ S ∣ |S| S的值, n + 1 , … , n 2 n+1,\dots,n^2 n+1,,n2也没有影响.因此最终答案需要乘 n ! 和 ( n 2 − n ) ! n!和(n^2-n)! n!(n2n)!

接下来,分别考虑 1 , 2 , 3 … , n 1,2,3\dots,n 1,2,3,n每个数的贡献.

考虑数字 1 , 2 , 3 … , n 1,2,3\dots,n 1,2,3,n提供的贡献,即这一行的其他数字都要比他大,因此共有 C n 2 − i n − 1 C_{n^2-i}^{n-1} Cn2in1种不同选法.而数字 i i i在这一行中有 n n n种选择。

a n s = ∑ i = 1 n n ∗ C n 2 − i n − 1 ∗ n ! ∗ ( n 2 − n ) ! ans = \sum_{i=1}^nn*C_{n^2-i}^{n-1}*n!*(n^2-n)! ans=i=1nnCn2in1n!(n2n)!

(如果处理阶乘的时候分段打表还能更快)

(upd:还可以直接打答案的表)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 3e7, mod = 998244353;
int T, n;
ll fact[MAXN], infact[MAXN];

ll qmi(ll a, ll b)
{
    ll res = 1;
    while(b)
    {
        if(b & 1) res = res * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return res;
}

void init()
{
    fact[0] = infact[0] = 1;
    for(int i = 1;i <= 25000000;i++)
    {
        fact[i] = fact[i - 1] * i % mod;
        infact[i] = infact[i - 1] * qmi(i, mod - 2) % mod;
    }
    return ;
}

ll C(ll m, ll n)
{
    if(m < n) return 0;
    return fact[m] * infact[n] % mod * infact[m - n] % mod;
}

ll A(ll m, ll n)
{
    if(m < n) return 0;
    return fact[m] * infact[m - n] % mod;
}

/*
int main()
{
    init();
    for(int i = 1;i <= 5000;i++)
    {
        ll ans = 0;
        for(int k = 1;k <= i;k++)
        {
            ans = (ans + i * fact[i] % mod * fact[i * i - i] % mod * C(i * i - k, i - 1) % mod) % mod;
        }
        printf("%lld,", ans);
    }

    return 0;
}
*/

int main()
{
    cin >> T;
    while(T--)
    {
        scanf("%d", &n);
        printf("%d\n", a[n]);
    }
    return 0;
}

K (并查集)

按询问从小到大进行排序,并同时建树,计算所有连通块大小即可。

注意:每次在更新完后需要给ans赋值

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 2e5+50;
int T, n, m, q;

struct edge{
    int u, v, w;
    bool operator<(const edge &e) const
    {
        return w > e.w;
    }
}E[MAXN];

int fa[MAXN];
ll sz[MAXN];

struct query
{
    int qq, id;
    ll ans;
    bool operator<(const query &quer) const{
        return qq > quer.qq;
    }
}qu[MAXN];

bool cmp(query a, query b)
{
    return a.id < b.id;
}

int find(int x)
{
    if(x != fa[x]) fa[x] = find(fa[x]);
    return fa[x];
}

ll getans(ll x)
{
    return x * (x-1) / 2;
}

int main()
{
    cin >> T;
    while(T--)
    {
        scanf("%d%d%d", &n, &m, &q);
        for(int i = 1;i <= n;i++) {
            fa[i] = i;
            sz[i] = 1;
        }
        for(int i = 1;i <= m;i++)
        {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            E[i] = {u, v, w};
        }
        sort(E+1, E+1+m);
        for(int i = 1;i <= q;i++)
        {
            int x;
            scanf("%d", &x);
            qu[i] = {x, i};
        }
        sort(qu+1, qu+1+q);

        // 从大到小建图

        ll res = 0;
        int idx = 1;

        for(int i = 1; i <= q; i++)
        {
            while(E[idx].w >= qu[i].qq && idx <= m)
            {
                int u = E[idx].u, v = E[idx].v;
                int uu = find(u), vv = find(v);
                if(uu == vv) {
                    idx ++; continue;
                }
                fa[uu] = vv;
                ll sz1 = sz[vv], sz2 = sz[uu];
                sz[vv] += sz[uu];
                idx++;
                res = res + getans(sz[vv]) - getans(sz1) - getans(sz2);
            }
            qu[i].ans = res;
        }
        sort(qu+1, qu+1+q, cmp);
        for(int i = 1;i <= q;i++) printf("%lld\n", qu[i].ans);

    }
    return 0;
}


/*
2
5 5 3
1 2 2
2 3 3
3 4 1
4 5 1
5 1 3
3
2
1
5 5 3
1 2 2
2 3 3
3 4 1
4 5 1
5 1 3
1
1
1

*/

D(线段树)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值