BZOJ4042 : [Cerc2014] parades

设f[x]为x子树里能选的最多的路径数,h[x]为x子树里往上走的点的集合,且不与x子树内的最优解冲突

首先f[x]=sum(f[son])

若h[son]与x可以直接匹配,则匹配上,f[x]++

然后把剩下的未配对的son之间进行匹配,f[x]+=最大匹配数

因为度数不超过10,所以设dp[S]表示二进制表示为S的集合里的最大匹配,x=lowbit(S),则

dp[S]=max(dp[S^(1<<x)],dp[S^(1<<x)^(1<<y)]+1),其中y属于S,y>x,且x与y可以匹配

若dp[(1<<t)-1]==dp[((1<<t)-1)^(1<<i)],则表明i不在最优解中,需要将其加入h[x]中

时间复杂度$O(n2^{10})$。

#include<cstdio>
const int N=1010,K=10;
int T,n,m,i,x,y,f[N],q[K],t,a[K][K],dp[1<<K];bool e[N][N];
struct E{int v;E*nxt;}*g[N],*h[N],pool[1010000],*cur=pool;
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 addg(int x,int y){E*p=cur++;p->v=y;p->nxt=g[x];g[x]=p;}
inline void addh(int x,int y){E*p=cur++;p->v=y;p->nxt=h[x];h[x]=p;}
inline bool match(int x,int y){
  for(E*i=h[x];i;i=i->nxt)for(E*j=h[y];j;j=j->nxt)if(e[i->v][j->v])return 1;
  return 0;
}
inline void up(int&a,int b){if(a<b)a=b;}
void dfs(int x,int y){
  f[x]=0,h[x]=NULL;
  for(E*i=g[x];i;i=i->nxt)if(i->v!=y)dfs(i->v,x),f[x]+=f[i->v];
  t=0;
  for(E*i=g[x];i;i=i->nxt)if(i->v!=y){
    bool flag=1;
    for(E*j=h[i->v];j;j=j->nxt)if(e[x][j->v]){f[x]++,flag=0;break;}
    if(flag)q[t++]=i->v;
  }
  for(int i=0;i<t;i++)for(int j=i+1;j<t;j++)a[i][j]=match(q[i],q[j]);
  int F=(1<<t)-1;
  for(int S=1;S<=F;S++){
    int i=__builtin_ctz(S&-S);
    dp[S]=dp[S^(1<<i)];
    for(int U=S-(S&-S);U;U-=U&-U){
      int j=__builtin_ctz(U&-U);
      if(a[i][j])up(dp[S],dp[S^(1<<i)^(1<<j)]+1);
    }
  }
  f[x]+=dp[F],addh(x,x);
  for(int i=0;i<t;i++)if(dp[F]==dp[F^(1<<i)])for(E*j=h[q[i]];j;j=j->nxt)addh(x,j->v);
}
int main(){
  for(read(T);T--;printf("%d\n",f[1])){
    for(read(n),i=1;i<n;i++)read(x),read(y),addg(x,y),addg(y,x);
    for(read(m);m--;e[x][y]=e[y][x]=1)read(x),read(y);
    dfs(1,0);
    for(cur=pool,i=1;i<=n;i++)for(g[i]=NULL,x=1;x<=n;x++)e[i][x]=0;
  }
  return 0;
}

  

时间: 2024-10-06 00:10:54

BZOJ4042 : [Cerc2014] parades的相关文章

BZOJ4044 : [Cerc2014] Virus synthesis

设f[x]表示得到x这个回文串的最小步数,则ans=min(n-len[x]+f[x]) 边界条件f[长度为0的偶回文串]=1 因为翻转只会得到偶回文串,所以f[奇回文串]=该串的长度 对于一个偶回文串x,设y为x去掉首尾得到的串,有f[x]=f[y]+1 设y为长度不超过x的一半的x的最长回文后缀,有f[x]=len[x]/2-len[y]+f[y]+1 两种情况取个最小值即可. 对于状态的表示以及最长回文后缀的询问,用回文树支持操作即可.时间复杂度$O(n)$. #include<cstdi

BZOJ4049 [Cerc2014] Mountainous landscape

首先对于一个给定的图形,要找到是否存在答案非常简单... 只要维护当然图形的凸包,看一下是否有线段在这条直线上方,直接二分即可,单次询问的时间复杂度$O(logn)$ 现在用线段树维护凸包,即对于一个区间$[l, r]$,我们维护点$[P_l, P_{r +1}]$形成的凸包 于是每次查询只要在线段树上二分,总复杂度$O(nlogn + nlog^2n)$(建树 + 查询) 1 /**********************************************************

BZOJ4046 [Cerc2014] Pork barre

我们把边按权值从大到小依次加入图中 如果加到边权$V$,则当前的最小生成森林中边权$v\in[V, V']$(其中$V'$是任意值)形成的森林的边权和就是对于询问$[V, V']$的答案 由于点数不多,所以可以每次暴力$dfs$找环上最大边以及暴力删除... 又由于是强制在线,于是用可持久化线段树维护不同权值的出现次数即可 1 /************************************************************** 2 Problem: 4046 3 Use

BZOJ4045 : [Cerc2014] bricks

首先求出B和W的个数,如果只出现了一种那么直接输出sum(k). 否则依次扫描,能割就割,时间复杂度$O(n)$. #include<cstdio> #define N 100010 typedef long long ll; int T,n,i,a[N],b[N],c[2],d[2],ans;ll x;char s[2]; int main(){ scanf("%d",&T); while(T--){ scanf("%d",&n); c

bzoj4042

比较好的树形dp,涉及到树上路径的题目,我们往往考虑对路径分类 当我们考虑以x为根的子树,有这样几类路径 1. 起点终点都在子树内 2. 一个点延伸到子树外 对于要选择另一个点在子树外的路径,要建立在不破坏儿子子树内的路径基础上 因为破坏会破坏多条,而只能多选择一条,不合适 因此我们考虑树dp,设f[x]为路径起点终点在子树内能选择的最多路径数目,d[x]表示还没用过的另一个点在x子树外的路径的集合 我们知道,对于x的每个孩子d,d[y]中最多只会有1条路径被选中,因此我们可以用状压dp解决 1

bzoj 3928: [Cerc2014] Outer space invaders

$f[i][j]$表示消灭起始时间在$(i,j)$内的外星人所花费的最小代价. 考虑在这个区间内距离最远的外星人h,在他的区间中一定要选一个点要开一炮,而且这一炮可以顺便把其他跨过这个点的敌人消灭,剩下只需消灭没有跨过这个点的敌人. 枚举开炮时间$f[i][j]=min(f[i][k]+f[k][j]+d[h])$. 区间需要离散化. #include<iostream> #include<cstdio> #include<cstring> #include<al

BZOJ3928 [Cerc2014] Outer space invaders

第一眼,我勒个去...然后看到n ≤ 300的时候就2333了 首先把时间离散化,则对于一个时间的区间,可以知道中间最大的那个一定要被选出来,然后把区间分成左右两份 于是区间DP就好了,注意用左开右开的区间比较方便2333 如果暴力找区间内最大值是O(n3)的,当然st表就是O(n2logn)的了...不过st表辣么难蒟蒻根本不会QAQQQ 1 /************************************************************** 2 Problem: 3

luogu P4762 [CERC2014]Virus synthesis (回文自动机)

大意: 初始有一个空串, 操作(1)在开头或末尾添加一个字符. 操作(2)在开头或末尾添加该串的逆串. 求得到串$S$所需最少操作数. 显然最后一定是由某个偶回文通过添加字符得到的, 那么只需要求出所有偶回文的最少操作数即可. 结论: 偶回文最后一次进行翻倍操作一定最优. 证明考虑数学归纳, 对于长为$2$的回文串显然成立. 对长度$>2$的偶回文串$A$, 记最后一次翻倍得到的串$B$, $B$的一半为$C$. 记$f(S)$为串$S$的最优值, 就有$f(B)=f(C)+1$. 考虑由$B$

P4762 [CERC2014]Virus synthesis

题意 真是道回文自动机好题. 首先考虑答案必定是一个回文串+剩余部分的形式,因此可以建出回文自动机,之后考虑每个长度为偶数的回文串. 对于一个长度为偶数的回文串,设它在回文自动机上对应的节点为\(x\),我们对于每个\(x\)求出\(trans_x\)表示x的最长后缀回文串,满足\(len_{trans_x}\leqslant len_x/2\). 之后设\(f_x\)表示\(x\)拼成\(x\)这个串的最小代价,我们从\(0\)(偶根)出发进行\(bfs\),中途计算\(f_x\). 对于\(