@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形). 问题变 ...
随机推荐
- PHP之文件目录基础操作方法
1.文件的属性信息获取 首先文件具有类型,在linux下边,有block(块设备,如磁盘分区.CD-ROM).char(以字符为输入的设备,如键盘.打印机).dir(目录类型,目录也是文件的一种).f ...
- webpack学习之——Entry Points(入口起点)
1.Entry property(entry属性) 1.1 Single Entry (Shorthand) Syntax(单个入口语法) 用法:entry: string | Array<st ...
- js中保存成图片并下载
1.保存canvas中绘制的内容为图片 HTML代码: <canvas id="canvas" width="400" height="400& ...
- !important覆写css行内样式
<div class="block"> <span style="font-weight: bold; color: red;">Hel ...
- django的admin后台管理
Admin后台管理 要进入admin后台管理首先要创建管理员账户 createsuperuser 其中密码要大于8位 使用之前要到应用下的admin.py中注册要管理的模型表 from django. ...
- 【转载】【技巧总结】PyCharm怎么克隆github上开源的项目
PyCharm怎么clone github上开源的项目 一.先要确保PyCharm正确的配置了Git 如果你已经在PyCharm中配置好了Git,可以跳过此步骤,直接看下一步. 那么怎么在Py ...
- 【Leetcode 堆、快速选择、Top-K问题 BFPRT】有序矩阵中第K小的元素(378)
题目 给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第k小的元素. 请注意,它是排序后的第k小元素,而不是第k个元素. 示例: matrix = [ [ 1, 5, 9], [ ...
- LeafLet 简单使用
Leaflet 使用 最近在Angular项目中,用到了地图,由于种种原因放弃了百度地图api使用,最后选择了leaflet,简单介绍一下. 介绍: Leaflet 是一个为移动设备设计的交互式地图的 ...
- Auto reloading enabled
在eclipse中集成tomcat来开发时, 如果使用run as模式启动项目的话,tomcat配置Auto reloading enabled,我们修改java文件,项目会重新加载,修改的内容会生效 ...
- oracle-Expdp/impdp命令
建立逻辑路径 create or replace directory dumpdir as 'c:\'; grant read,write on directory dumpdir to scott; ...