@noi.ac - 442@ 牛羊被他抢了
@description@
众所周知小G热衷于搏弈,有一天他来到你的大草原上,抢走了你所有的牛羊,以供他搏弈。
你大惊失措,赶紧来到小G家拯救你的牛羊,然而,当你赶到的时候,你的牛已经变成了搏弈牛,你的羊已经变成了搏弈羊。你怒不可遏,决定拆了小G的家。
小G的家有 n 个房间,n−1 条走廊连接这些房间使得房间之间两两可达。在每一间房间里可能有一只牛,或者一只羊,或者一只小G,注意由于小G会分身术,所以可以有多个小G。
经过慎重考虑,你决定选择一些房间来攻击,你选择的房间必须满足两两之间可以不通过你未选择的房间到达。当你攻击时,你选择的房间中的所有牛、羊和小G都会来与你搏弈,由于人的力量是有限的,你只能应对 A 只牛,B 只羊,C 只小G。也就是说你能够同时应对 u 只牛,v 只羊和 w 只小G当且仅当 u≤A 且 v≤B 且 w≤C。
你想知道有多少种选择房间的方案使得你可以应对他们,输出其对 998244353 取模的结果。
@Input format@
第一行四个数 n,A,B,C,含义如题。
第二行 n 个数,第 i 个数表示第 i 间房里是什么:0 代表牛,1 代表羊,2 代表小G。
接下来 n−1 行,每行两个数 u,v,表示 u 到 v 之间有一条走廊。
@Output format@
一个数表示答案。
@Sample input@
5 1 1 1
0 1 0 0 2
1 2
1 3
3 4
4 5
@Sample output@
7
@Constraints@
对于所有的数据,n≤200,A+B+C≤n/2。
@solution@
任轩笛的论文《解决树上连通块问题的一些技巧和工具》中有提到与这道题类似的题并给出了解决方案:
对于一类树上连通块DP问题,如果信息的合并不够高效,但加入单点的信息比较高效,往往可以按照DFS序转移,把合并子树变成添加单点。
先假定选出的连通块必须包含根,求出整棵树的DFS序,一个包含根的树上连通块的结构一定形如整棵树去掉若干个互不相交的子树。在DFS序中,去掉的就是连续的若干段。
设 dp[i] 表示考虑了DFS序前 i 个节点时的信息。如果要选择第 i 个节点,则转移到 dp[i+1],否则转移到 dp[j],其中 j 是第 i 个点子树DFS序的右端点+1,表示去掉这整个子树。
而对于选出的连通块可以不包含根的问题,只要进行点分治,每次把重心作为根,算
出强制包含重心的方案数,再把重心删除,对每个连通块递归做。
之后论文中给出的例题也与这道题大同小异。
题意:给出一棵树,每个点有个颜色。给出3种颜色 u, v, w ,求有多少个树上连通块满足里面颜色为 u, v, w 的点的个数分别为 a, b, c 。
做法:用 dp[x][i][j][k] 表示处理了以 x 为根的子树,三种颜色个数分别为 i, j, k 的方案数。如果使用三维FFT优化,复杂度能变成 O(nabc log abc) ,但是常数和码量都较大。考虑使用上述的点分治算法,配合按DFS序转移,复杂度为 O(nabc log n)。
然后论文的题解基本照搬过来即可。
因为 A + B + C <= n/2 <= 100,所以 A*B*C 最大时 A = B = C,可以得到 A*B*C 最大约可以取 3*10^4。
所以 O(nABC*logn) 是可以跑得过的。
所以这是一道论文题。
@accepted code@
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 200;
const int MOD = 998244353;
struct edge{
int to; edge *nxt;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt=&edges[0];
void addedge(int u, int v) {
edge *p = (++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
p = (++ecnt);
p->to = u, p->nxt = adj[v], adj[v] = p;
}
int siz[MAXN + 5], hvy[MAXN + 5], dfn[MAXN + 5], dcnt;
int a[MAXN + 5], n, A, B, C;
bool vis[MAXN + 5];
void dfs(int x, int fa) {
dfn[++dcnt] = x, siz[x] = 1, hvy[x] = 0;
for(edge *p=adj[x];p;p=p->nxt) {
if( vis[p->to] || p->to == fa ) continue;
dfs(p->to, x);
siz[x] += siz[p->to];
hvy[x] = max(hvy[x], siz[p->to]);
}
}
int GetG(int x, int fa, int tot) {
int ret = x; hvy[x] = max(hvy[x], tot - siz[x]);
for(edge *p=adj[x];p;p=p->nxt) {
if( vis[p->to] || p->to == fa ) continue;
int tmp = GetG(p->to, x, tot);
if( hvy[tmp] < hvy[ret] )
ret = tmp;
}
return ret;
}
int dp[MAXN + 5][34*34*35 + 5], ans = 0;
inline int id(int x, int y, int z) {
return x*(B+1)*(C+1) + y*(C+1) + z;
}
void divide(int x) {
vis[x] = true, dcnt = 0, dfs(x, -1);
for(int p=1;p<=dcnt+1;p++)
for(int i=0;i<=A;i++)
for(int j=0;j<=B;j++)
for(int k=0;k<=C;k++)
dp[p][id(i, j, k)] = 0;
dp[1][id(0, 0, 0)] = 1;
for(int p=1;p<=dcnt;p++)
for(int i=0;i<=A;i++)
for(int j=0;j<=B;j++)
for(int k=0;k<=C;k++) {
dp[p+siz[dfn[p]]][id(i, j, k)] = (dp[p+siz[dfn[p]]][id(i, j, k)] + dp[p][id(i, j, k)])%MOD;
if( a[dfn[p]] == 0 && i != A ) dp[p+1][id(i+1, j, k)] = (dp[p+1][id(i+1, j, k)] + dp[p][id(i, j, k)])%MOD;
if( a[dfn[p]] == 1 && j != B ) dp[p+1][id(i, j+1, k)] = (dp[p+1][id(i, j+1, k)] + dp[p][id(i, j, k)])%MOD;
if( a[dfn[p]] == 2 && k != C ) dp[p+1][id(i, j, k+1)] = (dp[p+1][id(i, j, k+1)] + dp[p][id(i, j, k)])%MOD;
}
for(int i=0;i<=A;i++)
for(int j=0;j<=B;j++)
for(int k=0;k<=C;k++)
ans = (ans + dp[dcnt + 1][id(i, j, k)])%MOD;
ans = (ans + MOD - 1)%MOD;
for(edge *p=adj[x];p;p=p->nxt)
if( !vis[p->to] ) divide(GetG(p->to, -1, siz[p->to]));
}
int main() {
scanf("%d%d%d%d", &n, &A, &B, &C);
for(int i=1;i<=n;i++)
scanf("%d", &a[i]);
for(int i=1;i<n;i++) {
int u, v; scanf("%d%d", &u, &v);
addedge(u, v);
}
dcnt = 0, dfs(1, -1), divide(GetG(1, -1, siz[1]));
printf("%d\n", ans);
}
@details@
所以多读书论文是有好处的。
只不过我估计 noi 上是不会出现这种题的吧。。。
@noi.ac - 442@ 牛羊被他抢了的更多相关文章
- # NOI.AC省选赛 第五场T1 子集,与&最大值
NOI.AC省选赛 第五场T1 A. Mas的童年 题目链接 http://noi.ac/problem/309 思路 0x00 \(n^2\)的暴力挺简单的. ans=max(ans,xor[j-1 ...
- NOI.ac #31 MST DP、哈希
题目传送门:http://noi.ac/problem/31 一道思路好题考虑模拟$Kruskal$的加边方式,然后能够发现非最小生成树边只能在一个已经由边权更小的边连成的连通块中,而树边一定会让两个 ...
- NOI.AC NOIP模拟赛 第五场 游记
NOI.AC NOIP模拟赛 第五场 游记 count 题目大意: 长度为\(n+1(n\le10^5)\)的序列\(A\),其中的每个数都是不大于\(n\)的正整数,且\(n\)以内每个正整数至少出 ...
- NOI.AC NOIP模拟赛 第六场 游记
NOI.AC NOIP模拟赛 第六场 游记 queen 题目大意: 在一个\(n\times n(n\le10^5)\)的棋盘上,放有\(m(m\le10^5)\)个皇后,其中每一个皇后都可以向上.下 ...
- NOI.AC NOIP模拟赛 第二场 补记
NOI.AC NOIP模拟赛 第二场 补记 palindrome 题目大意: 同[CEOI2017]Palindromic Partitions string 同[TC11326]Impossible ...
- NOI.AC NOIP模拟赛 第一场 补记
NOI.AC NOIP模拟赛 第一场 补记 candy 题目大意: 有两个超市,每个超市有\(n(n\le10^5)\)个糖,每个糖\(W\)元.每颗糖有一个愉悦度,其中,第一家商店中的第\(i\)颗 ...
- NOI.AC NOIP模拟赛 第四场 补记
NOI.AC NOIP模拟赛 第四场 补记 子图 题目大意: 一张\(n(n\le5\times10^5)\)个点,\(m(m\le5\times10^5)\)条边的无向图.删去第\(i\)条边需要\ ...
- NOI.AC NOIP模拟赛 第三场 补记
NOI.AC NOIP模拟赛 第三场 补记 列队 题目大意: 给定一个\(n\times m(n,m\le1000)\)的矩阵,每个格子上有一个数\(w_{i,j}\).保证\(w_{i,j}\)互不 ...
- NOI.AC WC模拟赛
4C(容斥) http://noi.ac/contest/56/problem/25 同时交换一行或一列对答案显然没有影响,于是将行列均从大到小排序,每次处理限制相同的一段行列(呈一个L形). 问题变 ...
随机推荐
- 『StabilityGuide』| 10+位阿里技术专家共同发起稳定性知识库开源项目
我们穿过山和大海,也见过人山人海.我们见过各类故障,也排过千雷万险.这一次,不如我们一起,开启稳定性的探索之旅.让无法解决的问题少一点点,让世界的确定性多一点点. 无论是前端业务的开发者,还是后端架构 ...
- 利用 awk 将当前的链接按端口汇总倒排序
写了一行命令,利用 awk 将当前的链接按端口汇总倒排序 :) netstat -ano | awk /tcp.*:1[15].*:[1-5]/'{print $4}' | awk -F ':' ' ...
- cronexpr任务调度
package main import ( "github.com/gorhill/cronexpr" "fmt" "time" ) fun ...
- RestFul 与 RPC
原文地址:https://blog.csdn.net/u014590757/article/details/80233901 RPC.REST API深入理解 一:RPC RPC 即远程过程调用(Re ...
- 忘记用了delete释放内存,如何防止内存溢出
C++的内存管理还是要自己来做的,自己要进行内存的申请和释放 程序直接kill掉,OS会回收的 但是面试要问到这个问题,其实是想问你别的 RAII,也称为“资源获取就是初始化”,是c++等编程语言常用 ...
- 虚幻UE4的后处理特效介绍 http://www.52vr.com/thread-31215-1-1.html
转载 虚幻UE4提供了后处理特效的功能,可以实现景深,光溢出,色调调整,饱和度等等.要使用虚幻4的后处理,就一定要用到PostProcessVolumn,这是一种特殊的体积,可以放置在场景中的任何位置 ...
- List.Sort 排序用法收集
使用Lambda表达式,实现代码如下: private static void SortByLambda() { List<Article> list ...
- Directx11教程(21) 修正程序最小化异常bug
原文:Directx11教程(21) 修正程序最小化异常bug 很长时间竟然没有注意到,窗口最小化时候,程序会异常,今天调试水面程序时,随意间最小化了窗口,发现程序异常了.经过调试,原来程 ...
- 网络流24题 负载平衡(DCOJ8013)
题目描述 G 公司有 n nn 个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等.如何用最少搬运量可以使 n nn 个仓库的库存数量相同.搬运货物时,只能在相邻的仓库之间搬运. 输入格式 文件 ...
- 获取表单所有字段 Post
var params = $(".layui-form").serializeArray(); var values = {}; for (x in params) { value ...