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]=∏v∈son[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]=∏v∈son[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]=∏v∈son[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!和(n2−n)!
接下来,分别考虑 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} Cn2−in−1种不同选法.而数字 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=1nn∗Cn2−in−1∗n!∗(n2−n)!
(如果处理阶乘的时候分段打表还能更快)
(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
*/