习题:Karen and Supermarket(树DP)

题目

传送门

思路

很容易就能将其转换成一个树上的问题,

这道题的题面就已经有一点像背包问题了,但是如果直接用背包的话,时间复杂度就会卡在容量那一层

所以我们换一种思路\(dp[i][j][0/1]\)表示以i为根节点的子树,买了j个物品,i是否使用优惠券,所使用的的最小金额

这个转移相信读者都会,下面我们来讨论时间复杂度的问题

一眼看上去这是一个\(O(n^3)\)的DP,但实则不然

我们仔细分析一下,对于一个子树,物品最多也就只有子树大小个

我们如果用插入的方式来转移这个DP

那么每一次的转移即为\(O(之前的子树大小*现在的子树大小)\)

我们换一种方式考虑

新的子树的每一个节点,会和每一个之前处理过的节点提供\(O(1)\)的贡献,但是这个贡献只会提供一次

换言之,每一对点,只会在他们的\(lca\)提供一次的贡献

所以总的时间复杂度应该为\(O(n^2)\)

代码

#include<iostream>
#include<vector>
using namespace std;
const int mod=998244353;
int n;
long long  dp[300005][3];
/*
0:匹配
1:未被匹配,但是父亲和他进行匹配
2:无子
*/
long long temp[3];
vector<int> g[300005];
void dfs(int u,int fa)
{
    dp[u][2]=1;
    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(v!=fa)
        {
            dfs(v,u);
            temp[0]=dp[u][0]*(dp[v][0]*2+dp[v][2])%mod;
            temp[0]=(temp[0]+(dp[u][1]+dp[u][2])*(dp[v][1]+dp[v][2])%mod)%mod;
            temp[1]=dp[u][2]*dp[v][0]%mod;
            temp[1]=(temp[1]+dp[u][1]*(dp[v][0]*2+dp[v][2]))%mod;
            temp[2]=(dp[u][2]*(dp[v][2]+dp[v][0]))%mod;
            for(int j=0;j<=2;j++)
                dp[u][j]=temp[j];    
        }
    }
}
int main()
{
    cin>>n;
    for(int i=1,u,v;i<n;i++)
    {
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs(1,0);
    cout<<(dp[1][0]+dp[1][2])%mod;
    return 0;
}
posted @ 2020-07-31 12:43  loney_s  阅读(101)  评论(0)    收藏  举报