树上Dp
河流(树上Dp)
题意
给你n+1个点,n条边,保证构成一棵树,然后再这n个上建造k个中转站,(0默认有,所以是n 个点)。每个点有w\([i]\)个需要中转的物品,求最后全部中转完的最短距离。
难点
如果我枚举当前建造了多少个中转站,但是我们不知道当前这个点有没有中转站,所以我们需要开两个dp,把两种情况分开讨论,\(d[i][j][k]\)表示以 i 为根结点 已经建造了 j 个中转站 同时以 k 为中转结点的最小代价,所以我们根据出栈入栈的顺序,枚举当前的根节点。
转移
\(d[i][v1][k]\)=\(min(d[i][v1-v2][k]+d[j][v2][k],O.o)\)
\(d2[i][v1][fa]\)=\(min(d2[i][v1-v2][fa]+d[j][v2][u],O.o)\)
\(d[i][j][fa]\)=\(min(d2[i][j-1][fa],O.o)\)
Code
const int N=120,mod=1e9+7;
int lowbit(int x){return x&-x;}
int gcd(int a,int b){return a%b==0?b:gcd(b,a%b);}
int d[N][N][N];// 当前第i个 建了j个 同时以 k为伐木场(k!=i
int d2[N][N][N]; //当前第i个 建了j个 同时以 k为伐木场(k==i
int w[N];
vector<pi>v[N];// 存边
int s[N],tt;//用栈来找出 可能是 中转站的点。
int n,m,de[N];// de 是用来找出距离的
void dfs(int u,int pr)
{
s[++tt]=u;// 入栈
for(pi t:v[u])
{
int j=t.x,y=t.y;
if(j==pr)continue;
de[j]=de[u]+y;
dfs(j,u);
for(int now=1;now<=tt;now++)//
{
int fa=s[now];// 枚举 可能的中转站
for(int j2=m;j2>=0;j2--)// 从大到小,经典背包O.o
{
// 这一步就相当于预处理了。
d[u][j2][fa]+=d[j][0][fa];//先找出最差的情况。j中一个中转站都没有
d2[u][j2][fa]+=d[j][0][u];//同上
for(int j3=1;j3<=j2;j3++)
{
// 枚举 儿子中有多少个中转站,如果u是中转站,那么他的儿子肯定都去u,否则会去fa
d[u][j2][fa]=min(d[u][j2][fa],d[u][j2-j3][fa]+d[j][j3][fa]);
d2[u][j2][fa]=min(d2[u][j2][fa],d2[u][j2-j3][fa]+d[j][j3][u]);
}
}
}
}
// 然后处理一下 当前点到 可能是中转站的 距离。
for(int i=1;i<=tt;i++)
{
int fa=s[i];
for(int j=0;j<=m;j++)
{
d[u][j][fa]+=(de[u]-de[fa])*w[u];
if(j>=1)d[u][j][fa]=min(d[u][j][fa],d2[u][j-1][fa]);
// d2 为啥要减1? 因为d 保证u 能是根节点,然后他的子树中就只有j-1个了
}
}
tt--;//出栈
}
void solve()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int a,b,c;
cin>>a>>b>>c;
w[i]=a;
v[b].ps({i,c});
}
dfs(0,-1);
cout<<d[0][m][0]<<endl;
}
signed main()
{
kd;
int _;_=1;
//cin>>_;
while(_--)solve();
return 0;
}
括号树
题意
树上每个点上有一个括号,让你算出改点到根节点的合法的子序列个数,最后亦或起来。
经典小芝士
当我们记录了,一个点的最近的匹配的 ( 标为s[x] ,那么 这段区间的 代价就是 \(w[fa[s[x]]]\)+1,然后捏 如果我们按照dfs 序进行 出栈入栈的化这个括号序列也就对应了,这个简单路径上的括号序列。
思路
对于每个点,记录一下他的结果,和他的父节点和他的能匹配的最近的 ( 。然后递归处理,如果一个点 是( 那么它的 匹配的情况就 改成自己, 如果不是 ( 同时有 能匹配的 ) 就加上这一段的 值。最后算一个前缀和,因为他求的是子串,如果 我爹有,那我也有。O.o
void dfs(int u,int pr)
{
s[u]=s[fa[u]];// 把他的最近左括号标成它父亲的。
if(w[u]==1)s[u]=u;// 如果我自己是左括号。就记录一下我能匹配的最近的左括号是自己
else if(s[u])ans[u]=ans[fa[s[u]]]+1,s[u]=s[fa[s[u]]];
// 如果是 ) 同时有可以匹配的情况,那么他的情况就是 匹配的括号的上一个位置的 结果 +1.
// 加上上一步的情况 同时加上当前匹配成功的一种情况。
for(int j:v[u])
{
dfs(j,u);
}
}
Code
const int N=2e6+100,mod=1e9+7;
int lowbit(int x){return x&-x;}
int gcd(int a,int b){return a%b==0?b:gcd(b,a%b);}
int w[N];
int s[N];
int d[N],fa[N];
int ans[N];
vector<int>v[N];
void dfs(int u,int pr)
{
s[u]=s[fa[u]];
if(w[u]==1)s[u]=u;
else if(s[u])ans[u]=ans[fa[s[u]]]+1,s[u]=s[fa[s[u]]];
for(int j:v[u])
{
dfs(j,u);
}
}
void solve()
{
int n;cin>>n;
for(int i=1;i<=n;i++)
{
char x;cin>>x;
if(x=='(')w[i]=1;
else w[i]=0;
}
for(int i=2;i<=n;i++)
{
int x;cin>>x;
fa[i]=x;
v[x].ps(i);
}
dfs(1,0);
int res=ans[1];
for(int i=2;i<=n;i++)
{
// cout<<i<<" "<<ans[i]<<endl;
ans[i]+=ans[fa[i]];
res^=(i*ans[i]);
}
cout<<res<<endl;
}
signed main()
{
kd;
int _;_=1;
//cin>>_;
while(_--)solve();
return 0;
}
C. Vertex Deletion
题意:
你可以删除一些点,但要满足删除之后其他的剩余的点都要有至少一个点和他直接相邻
思路:
O.o
一开始想少了。我以为只要考虑当前点要不要然后进去讨论,发现根本转移不过来,因为在转移 当前点要的情况,不知道子树的情况。然后看了题解发现需要记录一下要的情况,当前的子树要的要的情况。剩下的都在代码注释中详细解释一下
Code
const int N = 1e5 + 100, mod = 998244353, INF = 1e10;
int lowbit(int x) { return x & -x; }
int gcd(int a, int b) { return a % b == 0 ? b : gcd(b, a % b); }
vector<int> v[N];
// 要各个结点的情况
/*
不删和全删 +2
表示i 点删除了
1 表示 i点没删除 但是有子树
2 表示 i点没删除 同时没有子树
如果当前点删了
他的儿子被删除了,要么就有其他的子树,不然不合法
d[u][0]=(d[j][0]*(d[j][1]));
如果他的没有被删除
d[u][2]好转移:d[u][2]=(d[u][2]*d[j][0]);因为没有子树 就要全部删除
d[u][1]直接算有点复杂,这个点没有被删除的全部情况就是
d[u][1]*(d[j][0]+d[j][1]+d[j][2]),
减去 d[u][2]也就是所有的情况-该点没被删除但是没有子树的情况== 该点没被删除但是有子树的情况
*/
int d[N][3];
void dfs(int u, int pr) {
d[u][0] = 1, d[u][1] = 1, d[u][2] = 1;
for (int j : v[u]) {
if (j == pr) continue;
dfs(j, u);
d[u][0] = d[u][0] * (d[j][1] + d[j][0]) % mod;
d[u][2] = (d[u][2] * d[j][0]) % mod;
d[u][1] = d[u][1] * (d[j][0] + d[j][1] + d[j][2]) % mod;
}
d[u][1] -= d[u][2];
d[u][1] = (d[u][1] % mod + mod) % mod;
}
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; i++)
v[i].clear(), d[i][0] = d[i][1] = d[i][2] = 0;
for (int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
v[a].ps(b);
v[b].ps(a);
}
dfs(1, -1);
cout << (d[1][0] + d[1][1]) % mod << endl;
}
signed main() {
kd;
int _;
_ = 1;
cin >> _;
while (_--) solve();
return 0;
}

浙公网安备 33010602011771号