BZOJ3547 : [ONTAK2010]Matchings

树形DP

f[i][0]表示不向下连边的最大匹配数

f[i][1]表示向下连一条边的最大匹配数

h[][]表示对应的方案数

为了防止爆栈用BFS

为了防止MLE:

1.数组循环利用,比如存边的数组在存完边后可以用来当dp数组

2.BFS时判断哪些点已经走过的bool数组改成bitset

3.能不开数组就不开数组,如前缀积

4.对于数值不超过n的数组,改成手写的3个unsigned char组合而成的数据类型,可以压缩1/4内存

5.记录方案的数组只要开int,计算时强制转化成long long

 

压了好久内存,终于从77M压到了31M…我发现bool占用空间居然和char是一样的!结构体占用内存是按照里面最大的那种数据类型为基准计算的。

 

#include<cstdio>
#include<bitset>
typedef long long ll;
const int N=1500001;
int n,m,i,j,x,y,g[N],v[N<<1],ed,head,tail,maxv,pre,now,t0,t1;
std::bitset<N>in;
struct Num{
  unsigned char a,b,c;
  Num(){a=b=c=0;}
  Num(int x){a=x&255,x>>=8,b=x&255,c=x>>8;}
}nxt[N<<1],q[N];
inline int Real(Num x){return (int)x.a|(int)x.b<<8|(int)x.c<<16;}
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
inline void add(int x,int y){v[++ed]=y;nxt[ed]=Num(g[x]);g[x]=ed;}
inline ll mul(ll a,ll b){return a*b%m;}
inline void max(int b){if(maxv<b)maxv=b;}
int main(){
  read(n);
  for(i=1;i<n;i++)read(x),read(y),add(x,y),add(y,x);
  read(m);
  in[Real(q[head=tail=1]=Num(1))]=1;
  while(head<=tail)for(i=g[x=Real(q[head++])],g[x]=tail+1;i;i=Real(nxt[i]))if(!in[v[i]])in[Real(q[++tail]=Num(v[i]))]=1;
  for(i=1;i<=n*2;i++)nxt[i]=Num(v[i]=0);
  for(i=n;i;i--){
    v[x=Real(q[i])]=1;
    if(g[x]>g[Real(q[i+1])]-1)continue;
    maxv=-N;head=g[x];tail=g[Real(q[i+1])]-1;now=0;
    for(j=head;j<=tail;j++){
      y=Real(q[j]),t0=Real(nxt[y]),t1=Real(nxt[y+n]);
      if(t0>t1){
        now+=t0;
        v[x]=mul(v[x],v[y]);
        v[y+n]=v[y];
        max(0);
      }else if(t0==t1){
        now+=t0;
        v[x]=mul(v[x],v[y]+v[y+n]);
        v[y+n]=(v[y]+v[y+n])%m;
        max(0);
      }else{
        now+=t1;
        v[x]=mul(v[x],v[y+n]);
        max(t0-t1);
      }
      g[y]=v[y+n];
    }
    nxt[x]=Num(now);now+=maxv+1;
    nxt[x+n]=Num(now);
    pre=g[Real(q[tail+1])]=1;
    for(j=tail-1;j>=head;j--)g[Real(q[j])]=mul(g[Real(q[j])],g[Real(q[j+1])]);
    for(j=head;j<=tail;j++){
      y=Real(q[j]),t0=Real(nxt[y]),t1=Real(nxt[y+n]);
      if(t0>=t1){
        if(maxv==0)v[x+n]=(v[x+n]+mul(mul(pre,g[Real(q[j+1])]),v[y]))%m;
      }else{
        if(maxv==t0-t1)v[x+n]=(v[x+n]+mul(mul(pre,g[Real(q[j+1])]),v[y]))%m;
      }
      pre=mul(pre,v[y+n]);
    }
  }
  if(Real(nxt[1])>Real(nxt[1+n]))printf("%d\n%d",Real(nxt[1]),v[1]%m);
  else if(Real(nxt[1])==Real(nxt[1+n]))printf("%d\n%d",Real(nxt[1]),(v[1]+v[1+n])%m);
  else printf("%d\n%d",Real(nxt[1+n]),v[1+n]%m);
  return 0;
}

  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值