LGJOJ 存放处
一直找不到原题。。。
树与连通块
有一棵树,n个结点,结点编号1至n,有n-1条边,第i条边连接的结点是u[i]和v[i]。
如果某个图满足如下的所有条件,那么这个图就是树的子图:
1、子图的所有结点都来自于树的结点,即子图的结点是树的结点的子集。
2、不妨假设子图的结点所形成的集合是S。
3、子图的边来自于树的边;对于树的每一条边,如果这条边连接的两个结点都是集合S的点,那么这条边就属于子图的边,否则这条边
不属于子图的边。
所有某个子图恰好含有i个连通块,那么这个子图就是"i子图"
你要回答n个问题,第i个问题是: 有多少个不同的"i子图"?答案模998244353。其中1<=i<=n。
输入格式 第一行,一个整数n。 1 <= n <= 5000。
接下来有n-1行,每行描述一条边,第i行是u[i]和v[i]。
输出格式 共n行,每行一个整数。
输入/输出例子1 输入:
4
1 2
2 3
3 4
输出:
10
5
0
0
输入/输出例子2 输入:
2
1 2
输出:
3
0
#include <bits/stdc++.h>
using namespace std;
const int MOD = 998244353, N = 5005;
int n, siz[N];
long long f[N][N][2], tmp[N][2];
vector<int> g[N];
void dfs(int u, int father) {
f[u][1][1] = 1;
f[u][0][0] = 1;
siz[u] = 1;
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if (v == father) continue;
dfs(v, u);
for (int k = 0; k <= siz[u]; k++) tmp[k][0] = f[u][k][0], tmp[k][1] = f[u][k][1];
for (int j = 0; j <= siz[u]; j++) {
for (int k = 0; k <= siz[v]; k++) {
if (j+k-1>=0) f[u][j+k-1][1] = (f[u][j+k-1][1]+tmp[j][1]*f[v][k][1])%MOD;
if(k!=0)f[u][j+k][1] = (f[u][j+k][1]+tmp[j][1]*f[v][k][0])%MOD;
f[u][j+k][0] = (f[u][j+k][0]+tmp[j][0]*f[v][k][1])%MOD;
if (k != 0) f[u][j+k][0] = (f[u][j+k][0]+tmp[j][0]*f[v][k][0])%MOD;
}
}
siz[u] += siz[v];
}
}
int main()
{
cin >> n;
for (int i = 0, u, v; i < n-1; i++) {
cin >> u >> v;
g[u].push_back(v); g[v].push_back(u);
}
dfs(1, -1);
for (int i = 1; i <= n; i++) {
cout << (f[1][i][0]+f[1][i][1])%MOD << endl;
}
return 0;
}
卡片
有n种类型的卡片,第i种类型的卡片的初始价值是a[i]元/张,第i种类型的卡片的限额是b[i]张。
额度的意思是你最多可以购买多少张这种类型的卡片。
有Q个操作,每个操作是如下的3种格式之一:
一、 格式为 1 x y, 表示把第x种类型的卡片的价值修改为y元/张。 1<=x<=n, 0 <= y <= 1e9。
二、 格式为 2 x y, 表示把第x种类型的额度修改为y。1<=x<=n, 0 <= y <= 1e9。
三、 格式为 3 x, 我们称这种操作是“查询”,表示假如你需要购买x张卡片,但每种卡片都不能超额度购买,
应该如何购买,使得购买到的x张卡片的总价值最大,输出最大总价值,如果无法购买满足条件,输出-1。 1<=x<=1e9。
注意:每个操作都是独立的,“查询”操作不是真的购买,只是为了让你回答问题而已。
输入格式 第一行,一个整数n, 1 <= n <= 2e5。
接下来有n行,第i行是a[i]和b[i],0<=a[i]<=1e9, 0<=b[i]<=1e4。
接下来一行是一个整数Q, 1<=Q<=2e5。
接下来是Q行,每行一个操作,每个操作是题目所述的3种操作之一。
输出格式 对于每一个“查询”操作,都输出一个整数,然后换行。
输入/输出例子1 输入:
3
1 1
2 2
3 3
7
3 4
1 1 10
3 4
2 1 0
2 3 0
3 4
3 2
输出:
11
19
-1
4
样例解释 对于第一个“查询”操作,购买1张第2种类型的卡片,再购买3张第3种类型的卡片,总价值是2+3*3=11。
对于第二个“查询”操作,购买1张第1种类型的卡片,再购买3张第3种类型的卡片,总价值是10+3*3=19
#include <bits/stdc++.h>
using namespace std;
const int N = 400005;
int n, a[N], b[N];
struct node {
long long sum, num;
} t[10*N];
int ls[10*N], rs[10*N];
int root = 0, cnt = 0;
void pushup(int r) {
t[r].sum=t[ls[r]].sum+t[rs[r]].sum;
t[r].num=t[ls[r]].num+t[rs[r]].num;
}
void modify(int &u, int l, int r, int pos, int val) {
if (!u) {
u = ++cnt;
t[u].sum = t[u].num = 0;
}
if (l ==r) {
t[u].num += val;
t[u].sum += (long long)val*l;
return;
}
int m = (l+r)>>1;
if (pos > m) modify(rs[u], m+1, r, pos, val);
else modify(ls[u], l, m, pos, val);
pushup(u);
}
long long query(int u, int l, int r, int p) {
if (!u) return 0;
//cout << "[" << l << "," << r << "] " << p << endl;
int m = (l+r)>>1;
if (t[u].num == p) {
return t[u].sum;
}
if (t[rs[u]].num >= p) {
return query(rs[u], m+1, r, p);
} else if (l != r) {
return query(ls[u], l, m, p-t[rs[u]].num)+query(rs[u], m+1, r, t[rs[u]].num);
} else {
return (long long)l*min((long long)p, t[u].num);
}
}
int main()
{
t[0].sum = t[0].num = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d%d", &a[i], &b[i]);
modify(root, 0, 1e9, a[i], b[i]);
}
int q;
scanf("%d", &q);
while (q--) {
int op, x, y;
scanf("%d", &op);
if (op == 1) {
scanf("%d%d", &x, &y);
modify(root, 0, 1e9, a[x], -b[x]);
a[x] = y;
modify(root, 0, 1e9, a[x], b[x]);
} else if (op == 2) {
scanf("%d%d", &x, &y);
modify(root, 0, 1e9, a[x], y-b[x]);
b[x] = y;
} else {
scanf("%d", &x);
if (t[root].num < x) printf("-1\n");
else printf("%lld\n", query(root, 0, 1e9, x));
}
}
return 0;
}
网络(2023.5.29)
有一个h行w列的网格,每个格子一开始都有一个数字0,有两种操作:
1、行操作,选中一行,0变1,1变0。
2、列操作,选择一列,0变1,1变0。
总共要进行r次行操作和c次列操作,目标是最后网格有s个1,有多少种不同的方案?答案模555555555。
同一行可以进行多次重复“行操作”,同一列可以进行多次“列操作”。
两种方案不同是指:存在某行或者某列,在两种方案中,操作的次数不同。
输入格式 多组测试数据。
第一行,一个整数g,表示有g组测试数据,g<=5。
每组测试数据格式:一行,5个整数, h, w,r,c,s。 1<=h,w<=1555, 0<=r,c<=1555, 0<=s<=h*w。
输出格式 共g行,每行一个整数。
输入/输出例子1 输入:
4
2 2 2 2 4
2 2 0 0 1
10 20 50 40 200
1200 1000 800 600 4000
输出:
4
0
333759825
96859710
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll MOD = 555555555;
ll C[3200][3200];
// 1 1200 1000 800 600 4000
int main()
{
C[0][0] = 1;
for (int i = 1; i < 3200; i++) {
C[i][0] = 1;
for (int j = 1; j < 3200; j++) {
C[i][j] = C[i-1][j-1]+C[i-1][j];
C[i][j] %= MOD;
}
}
int g;
cin >> g;
while (g--) {
ll h, w, r, c, s;
cin >> h >> w >> r >> c >> s;
ll ans = 0;
for (ll i = r%2; i <= min(h, r); i+=2) { // 实际变 i 行
for (ll j = c%2; j <= min(w, c); j+=2) { // 实际变 j 列
ll ts = i*w+j*(h-i)-i*j;
if (ts != s) continue;
ll ti = C[(r-i)/2+h-1][h-1], tj = C[(c-j)/2+w-1][w-1];
ans += (((C[h][i]*C[w][j])%MOD*ti)%MOD*tj)%MOD;
ans %= MOD;
}
}
cout << ans << endl;
}
return 0;
}
边的方向(2023.07.02)
有一个无向图,它有n个节点,有m条边,其中节点编号1至n,边的编号1至m。
第i条无向边连接u[i]和v[i]节点,数据保证没有重边,也没自环。
现在要对每一条无向边定方向,即变成有向图。
若没有任何限制,那么显然共有2^m种不同的方案,但是我们希望:
每个结点u,它的出边有且只有1条,那么有多少种不同的合法的方案?
答案模998244353。
输入格式 第一行,两个整数,n和m, 其中2<=n<=2e5, 1<=m<=2e5。
接下来有m行,第i行是u[i]和v[i]。1<=u[i],v[i]<=n。
输出格式 一个整数。
输入/输出例子1 输入:
3 3
1 2
1 3
2 3
输出:
2
输入/输出例子2 输入:
2 1
1 2
输出:
0
输入/输出例子3 输入:
7 7
1 2
2 3
3 4
4 2
5 6
6 7
7 5
输出:
4
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
#define ll long long
const ll MOD = 998244353;
vector<int> g[N];
int n, m;
int u[N], v[N], cnt[N];
int fa[N];
int find(int x) {
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void merge(int u, int v) {
u = find(u), v = find(v);
fa[u] = v;
}
ll po(ll x, ll n) {
ll t = 1;
while (n) {
if (n%2) t = t*x%MOD;
x = x*x%MOD;
n >>= 1;
}
return t;
}
bool del[N];
bool flag = 0;
void delet(int u) {
del[u] = 1;
cnt[u]--;
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if (del[v]) continue;
cnt[v]--;
if (cnt[v] == 1) delet(v);
}
}
bool chk(int u) {
if (cnt[u] != 2) return 0;
del[u] = 1;
int ret = 1;
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if (del[v]) continue;
ret &= chk(v);
}
return ret;
}
int main()
{
cin >> n >> m;
if (m < n) {
cout << 0 << endl;
return 0;
}
for (int i =1; i <= n; i++) fa[i] = i;
for (int i= 0; i <m; i++) {
cin >> u[i] >>v[i];
g[u[i]].push_back(v[i]);
g[v[i]].push_back(u[i]);
merge(u[i], v[i]);
cnt[u[i]]++;
cnt[v[i]]++;
}
for (int i = 1; i <= n; i++) {
if (g[i].size() == 0) {
cout << 0 <<endl;
return 0;
}
if (g[i].size() == 1) delet(i);
}
ll ans = 1;
for (int i = 1; i <= n; i++) {
if (del[i]) continue;
if (!chk(i)) {
cout << 0 << endl;
return 0;
}
ans *= 2;
ans %= MOD;
}
cout << ans << endl;
return 0;
}
游戏得分(2023.07.02)
设P是1至n的某个排列。有n个玩家参加游戏,编号1至n,还有一个裁判。
一开始,第i个小球在第i个玩家手上。
每次当裁判一吹哨,第i个玩家就会把他手上的球交给第P[i]个人,这n个人是同步进行的,
若i等于P[i],那么该玩家的球就相当于不用动。
裁判至少会吹第一次哨,那么游戏什么时候停止呢?
若对于所有的1<=i<=n都满足第i个球就在第i个人手上,那么游戏就会停止,
否则裁判就会再次吹哨,游戏继续,数据保证,游戏一定能停止。
对于某个特定的排列P,假设游戏结束时,裁判吹哨的次数为x,那么该排列的得分,其中k是读入的 。
因为P是1至n的某个排列,然而1至n的排列总共有n!个不同的排列,我们需要求出这n!个排列的总得分。
答案模998244353。
输入格式 第一行,两个整数,n和k。2<=n<=50, 1<=k<=10000。
输出格式 一个整数。
输入/输出例子1 输入:
2 2
输出:
5
输入/输出例子2 输入:
3 3
输出:
79
输入/输出例子3 输入:
50 10000
输出:
77436607
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 90;
const ll MOD = 998244353;
ll n, k;
ll po(ll x, ll n) {
ll t = 1;
while (n) {
if (n%2) t = t*x%MOD;
x = x*x%MOD;
n >>= 1;
}
return t;
}
ll fa[N], inv[N], invf[N];
ll f[N][N];
ll C(ll n, ll m) {
return fa[n]*invf[n-m]%MOD*invf[m]%MOD;
}
ll ans = 0;
ll cur[N];
void dfs(int len, int sum) {
if (sum == n) {
ll cnt = 1;
ll ts = 0, lcm = 1;
for (int i = 1; i < len; i++) {
ll j = i;
cnt = cnt*C(n-ts, cur[i])%MOD;
cnt = cnt*fa[cur[i]-1]%MOD;
ts += cur[i];
while (j+1 < len && cur[j+1] == cur[i]) {
j++;
cnt = cnt*C(n-ts, cur[i])%MOD;
cnt = cnt*fa[cur[i]-1]%MOD;
ts += cur[i];
}
cnt = cnt*invf[j-i+1]%MOD;
lcm = lcm*cur[i]/__gcd(lcm, cur[i]);
i = j;
}
ans = (ans+cnt*po(lcm, k)%MOD)%MOD;
return;
}
for (int i = max(1ll, cur[len-1]); sum+i <= n; i++) {
cur[len] = i;
dfs(len+1, sum+i);
}
}
int main()
{
inv[1] = inv[0] = invf[0] = invf[1] = fa[1] = fa[0] = 1;
for (int i = 2; i <= 80; i++) {
fa[i] = fa[i-1]*i%MOD;
inv[i] = (MOD-MOD/i)*inv[MOD%i]%MOD;
invf[i] = invf[i-1]*inv[i]%MOD;
}
cin >> n>> k;
dfs(1, 0);
cout <<ans << endl;
return 0;
}