ICPC2020 南京站

ICPC2020 南京站

H Harmonious Rectangle

简要题意

3染色,求满足存在轴向端点颜色相同矩形的染色方案个数。\(1 \leq n,m \leq 2000\)

题解

不妨设\(n\leq m\)。对于\(n=1\),构不成矩形无解。根据抽屉原理,当\(n \geq 2\)时,一行中前两个点染色情况只有\(9\)种,如果\(m > 9\),所有情况一定都满足条件。所以我们只需要考虑\(m \leq 9\)的情况,通过搜索计算不存在的方案数,事实上状态数很少,大多数情况在搜索中剪枝减去了。我的写法是暴力打表,但看其他人代码有的把情况数限制到\(7\),不知道为什么。

#include <bits/stdc++.h>
#define LL long long
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define lc nd<<1
#define rc nd<<1|1
#define lowbit(x) (x&(-x))
#define pLL pair<LL,LL>

using namespace std;

const int mn=1e5+6,mod=1e9+7;
int n,m,c[15][15];
LL cnt;
int ans[15][15]={
{},
{0,3,9,27,81,243,729,2187,6561,19683},
{0,9,66,390,1800,6120,13680,15120,0,0},
{0,27,390,3198,13176,27000,13680,15120,0,0},
{0,81,1800,13176,24336,4320,0,0,0,0},
{0,243,6120,27000,4320,4320,0,0,0,0},
{0,729,13680,13680,0,0,0,0,0,0},
{0,2187,15120,15120,0,0,0,0,0,0},
{0,6561,0,0,0,0,0,0,0,0},
{0,19683,0,0,0,0,0,0,0,0}
};

int read()
{
    int ans=0;char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) ans=ans*10+c-'0',c=getchar();
    return ans;
}

LL qpow(LL a,LL b) {
    LL ans=1;
    while(b) {
        if(b&1) ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}

void dfs(int x,int y)
{
    if(y>m) x++,y=1;
    if(x>n) {
        cnt++;
        return ;
    }
    for(int a=1;a<=3;++a) {
        c[x][y]=a;
        int flag=1;
        for(int i=1;i<x;++i) for(int j=1;j<y;++j) {
            if(c[i][y]==c[x][y]&&c[i][j]==c[x][j]) flag=0;
            if(c[x][j]==c[x][y]&&c[i][j]==c[i][y]) flag=0;
        }
        if(flag) dfs(x,y+1);
        c[x][y]=0;
    }

}

int main()
{
    int tests=1;scanf("%d",&tests);
    while(tests--) {
        n=read(),m=read();
        if(n>m) swap(n,m);
        if(n==1) {puts("0");continue;}
        if(m>=9) {
            printf("%lld\n",qpow(3,n*m));
        }
        else {
            if(ans[n][m]==-1) {
                cnt=0;
                dfs(1,1);
                ans[n][m]=cnt%mod;
            }
            printf("%lld\n",(qpow(3,n*m)+mod-ans[n][m])%mod);
        }
    }
    return 0;
}

M Monster Hunter

简要题意

给一棵树,每个点的权值是自身权值加上所有未删的儿子的权值,求删\(0-n\)个点分别对应的最小权值和。

题解

树形dp,考虑把贡献拆开,每个点的贡献会在自身删不删被考虑,在父亲删不删被考虑。三维的状态,\(f_{i,j,0/1}\)表示在\(i\)的子树内删\(j\)个点,\(0\)表示自身不删,\(1\)表示自身要删。

转移就是类似背包的转移,根据当前情况和儿子情况算和。

\[f_{i,j+k,0}=min(f_{i,j,0}+f_{t,k,0}+hp_t,f_{i,j,0}+f_{t,k,1}) \\ f_{i,j+k,1}=min(f_{i,j,1}+f_{t,k,0},f_{i,j,1}+f_{t,k,1}) \\ \]

这样转移是\(O(n^2)\)的,因为合并子树的过程只会做一次,两个点只会在lca合并一次。

#include <bits/stdc++.h>
#define LL long long
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define lc nd<<1
#define rc nd<<1|1
#define lowbit(x) (x&(-x))
#define pLL pair<LL,LL>

using namespace std;

const int mn=3005;
const LL inf=1e18;
vector<int> to[mn];
int hp[mn],n,siz[mn];
LL f[mn][mn][2],sum,tmp[mn];

void dfs(int x)
{
    for(int i=0;i<=n+1;++i) f[x][i][0]=f[x][i][1]=inf;
    siz[x]=1;
    f[x][0][0]=hp[x];
    f[x][1][1]=0;
    for(auto t:to[x]) {
        dfs(t);
        for(int i=0;i<=siz[x]+siz[t];++i) tmp[i]=inf;
        for(int i=0;i<=siz[x];++i) for(int j=0;j<=siz[t];++j) {
            tmp[i+j]=min(tmp[i+j],f[x][i][0]+f[t][j][1]);
            tmp[i+j]=min(tmp[i+j],f[x][i][0]+f[t][j][0]+hp[t]);
        }
        for(int i=0;i<=siz[x]+siz[t];++i) f[x][i][0]=tmp[i];
        for(int i=0;i<=siz[x]+siz[t];++i) tmp[i]=inf;
        for(int i=0;i<=siz[x];++i) for(int j=0;j<=siz[t];++j) {
            tmp[i+j]=min(tmp[i+j],f[x][i][1]+f[t][j][1]);
            tmp[i+j]=min(tmp[i+j],f[x][i][1]+f[t][j][0]);
        }
        for(int i=0;i<=siz[x]+siz[t];++i) f[x][i][1]=tmp[i];
        siz[x]+=siz[t];
    }
}

int main()
{
    int tests=1;scanf("%d",&tests);
    while(tests--) {
        sum=0;
        scanf("%d",&n);
        for(int i=1;i<=n;++i) to[i].clear();
        int fa;
        for(int i=2;i<=n;++i) {
            scanf("%d",&fa);
            to[fa].pb(i);
        }
        for(int i=1;i<=n;++i) {
            scanf("%d",&hp[i]);
        }
        dfs(1);
        for(int i=0;i<=n;++i) printf("%lld%c",min(f[1][i][0],f[1][i][1]),(i==n?'\n':' '));
    }
    return 0;
}
posted @ 2021-09-14 11:01  tianyy  阅读(329)  评论(0)    收藏  举报