Live2D

[HDU 6643] Ridiculous Netizens & [HDU 6566] The Hanged Man

这两题挺有意思的,而且比较相似,所以这里写一下。似乎这也是我省选之后第一篇题解。/kk

Ridiculous Netizens

题目传送门

Solution

首先可以发现可以树上背包 \(\Theta(nm^2)\) 直接合并。更进一步,我们考虑设 \(f_{u,i}\) 表示以 \(u\) 为根的连通块还能最多选 \(i\) 的方案数。可以发现的是 \(\lfloor \frac{n}{x} \rfloor\) 的形式不同的 \(i\) 只有 \(\sqrt m\) 种。所以我们可以做到 \(\Theta(nm)\)

这样肯定还是不够的。注意到我们如果可以一个点一个点加进去,那么其实可以做到 \(\Theta(n\sqrt m)\) 的。所以我们可以编出一个做法,就是对于每一个点 \(u\) 为根,然后往每个子树递归,设 \(f_{v,i}\) 表示选了考虑了 dfs 序在 \(v\) 之前且 \(v\) 子树选了之后还有 \(i\) 可以选的方案数,可以发现我们每次可以做到 \(\Theta(siz\times \sqrt m)\) ,其中 \(siz\) 是以 \(u\) 为根的子树的大小。

到这里下一步优化就很明显了,我们可以直接点分治,复杂度即是 \(\Theta(n\log n\sqrt m)\)

Code

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define mod 1000000007
#define MAXN 2015

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}

inline int mul (int a,int b){return 1ll * a * b % mod;}
inline int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
inline int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
inline int qkpow (int a,int b){
    int res = 1;for (;b;b >>= 1,a = mul (a,a)) if (b & 1) res = mul (res,a);
    return res;
}
inline void Add (int &a,int b){a = add (a,b);}
inline void Sub (int &a,int b){a = dec (a,b);}

vector <int> g[MAXN];
int n,m,lim,val[MAXN],tmp[MAXN],ind[1000005]; 

inline void link (int u,int v){g[u].push_back (v),g[v].push_back (u);  }

bool vis[MAXN];
int rt,siz[MAXN],mxs[MAXN];

inline void findroot (int u,int fa,int all){
    siz[u] = 1,mxs[u] = 0;
    for (Int v : g[u]) if (v != fa && !vis[v]) findroot (v,u,all),siz[u] += siz[v],chkmax (mxs[u],siz[v]);
    chkmax (mxs[u],all - siz[u]);
    if (!rt || mxs[u] < mxs[rt]) rt = u;
}

int f[MAXN][MAXN];
inline void getdp (int u,int fa){
    siz[u] = 1;
    for (Int v : g[u]) if (!vis[v] && v != fa){
        for (Int j = 0;j < m;++ j) f[v][j] = 0;
        for (Int j = 0;j < m;++ j){
            if (tmp[j] < val[v]) break;
            Add (f[v][ind[tmp[j] / val[v]]],f[u][j]);
        }
        getdp (v,u),siz[u] += siz[v];
        for (Int j = 0;j < m;++ j) Add (f[u][j],f[v][j]);
    }
}

int ans = 0;
inline void solve (int u){
    for (Int i = 0;i < m;++ i) f[u][i] = 0;
    vis[u] = 1,f[u][ind[lim / val[u]]] = 1,getdp (u,u);
    for (Int i = 0;i < m;++ i) Add (ans,f[u][i]);
    for (Int v : g[u]) if (!vis[v]) rt = 0,findroot (v,u,siz[v]),solve (rt);
}

inline void Work (){
    read (n,lim),m = ans = 0;
    for (Int u = 1;u <= n;++ u) vis[u] = 0,g[u].clear(),read (val[u]);
    for (Int l = 1,r;l <= lim;l = r + 1)
        r = lim / (lim / l),tmp[m] = lim / l,ind[tmp[m]] = m,m ++;
    for (Int i = 2,u,v;i <= n;++ i) read (u,v),link (u,v);
    rt = 0,findroot (1,0,n),solve (rt),write (ans),putchar ('\n');
} 

signed main(){
    int tim;read (tim);
    while (tim --> 0) Work ();
    return 0;
}

The Hanged Man

题目传送门

Solution

可以看出,直接做树上背包可以做到 \(\Theta(nm^2)\),显然过不了。

还是跟上一道题一样,如果我们可以一个点一个点地加,那么我们就可以做到 \(\Theta(nm)\)。但是这样的话我们就需要记录前面的点选的状态,这就是 \(2^n\) 了。

这个时候我们可以考虑通过改变加入顺序来优化复杂度。我们可以往重儿子方面考虑。所以递归到了 \(u\) 的时候,我们可以考虑先递归进重儿子,然后再加入 \(u\) 然后再考虑各个轻儿子,可以发现的是,处理完一个子树之后里面的点的状态就不需要记录了。我们假设 \(T(n)\) 表示 \(n\) 个点的树最多需要记录多少个点,可以发现对于重儿子而言就是 \(T(n-1)\),所以根本不会影响,考虑对于一个轻儿子,最多就有 \(T(n)=T(n/2)+1\),那么可以得出 \(T(n)=\log n\)

所以我们就以 \(2^{T(n)}\times n\times m=n^2\times m\) 的复杂度做完了这个题。

Code

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define mxt __int128
#define MAXM 5005
#define MAXN 55

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}

vector <int> g[MAXN];
int n,m,a[MAXN],b[MAXN];

#define inf 1e9

struct node{
    int val;mxt cnt;
    node(){val = -inf,cnt = 0;}
    node(int _v,mxt _cnt){val = _v,cnt = _cnt;}
    void clear(){val = -inf,cnt = 0;}
    node operator + (const int &p)const{return node(val + p,cnt);}
    node operator & (const node &p)const{
        if (val < p.val) return p;
        else if (val > p.val) return *this;
        else return node(val,cnt + p.cnt);
    }
};

int tot,ind[MAXN]={7};
node tmp[1 << 7][MAXM],f[1 << 7][MAXM];

void reduce (int p){//删掉p这个位置 
    tot --;
    for (Int x = 0;x < (1 << tot - p);++ x)
        for (Int y = 0;y < (1 << p);++ y)
            for (Int i = 0;i <= m;++ i) tmp[(x << p) | y][i] = f[(x << p + 1) | (1 << p) | y][i] & f[(x << p + 1) | y][i];
    for (Int s = 0;s < (1 << tot);++ s)
        for (Int i = 0;i <= m;++ i) f[s][i] = tmp[s][i];
}

int siz[MAXN],son[MAXN];
void dfs (int u,int fa){
    siz[u] = 1,son[u] = 0;
    for (Int v : g[u]) if (v ^ fa){
        dfs (v,u),siz[u] += siz[v];
        if (siz[v] > siz[son[u]]) son[u] = v;
    }
}

void solve (int u,int fa){
    ind[u] = 7;
    if (son[u]) solve (son[u],u);
    ind[u] = tot ++;
    for (Int s = 0;s < (1 << ind[u]);++ s){
        int ns = s | (1 << ind[u]);
        for (Int i = 0;i < a[u];++ i) f[ns][i].clear();
        if ((s >> ind[son[u]] & 1) | (s >> ind[fa] & 1)) for (Int i = a[u];i <= m;++ i) f[ns][i].clear();
        else for (Int i = a[u];i <= m;++ i) f[ns][i] = f[s][i - a[u]] + b[u]; 
    }
    if (son[u]) reduce (ind[u] = ind[son[u]]);
    for (Int v : g[u]) if (v != fa && v != son[u]) solve (v,u),reduce (ind[v]);
}

void Work (){
    read (n,m),tot = 0;
    for (Int u = 1;u <= n;++ u) read (a[u],b[u]),g[u].clear();
    for (Int i = 2,u,v;i <= n;++ i) read (u,v),g[u].push_back (v),g[v].push_back (u);
    for (Int u = 0;u <= n;++ u) for (Int i = 0;i <= m;++ i) f[u][i].clear();
    f[0][0] = node(0,1),dfs (1,0),solve (1,0),reduce (ind[1]);
    for (Int i = 1;i <= m;++ i){
        if (~f[0][i].val) write (f[0][i].cnt);
        else putchar ('0');
        if (i ^ m) putchar (' ');else putchar ('\n');
    }
}

signed main(){
    int tim;read (tim);
    for (Int k = 1;k <= tim;++ k)
        printf ("Case %d:\n",k),Work ();
    return 0;
}
posted @ 2022-08-31 10:50  Dark_Romance  阅读(49)  评论(0编辑  收藏  举报