[bzoj5404]party

https://zybuluo.com/ysner/note/1240918

题面

这题面不好概括啊

解析

(5pts)算法

既然(q=0),打上文件输入输出即可。
当然不开够空间且不特判的小朋友就拿不到了。

(15pts)算法

(nleq10,qleq10)
所有人在(lca)聚会是显然的吧(毕竟每个人只能往上走)。
这怎么乱搞都可以。
可以(O(m^c))枚举每个特产被哪个人带,再检查一下是否可行和合法(每个人带的特产数相同)即可。
复杂度似乎是(O(m^cclogn))???

(32pts)算法

(cleq2),两个人讨论起来比较方便。
先统计出三个信息(s_1,s_2,s_3)(s_1)表示两个人都可取到的特产数,(s_2)表示第一个人可取到的,(s_3)表示第二个人可取到的。
显然,若(s_1+s_2<s_3),则(ans=s_1+s_2)
(s_1+s_3<s_2),则(ans=s_1+s_3)
若以上均不满足,则(ans=(s_1+s_2+s_3)/2)
复杂度(O(qclogn))

(45pts)算法

(cleq3),估计出题人想让我们维护(6)个信息再讨论。。。
如果你能写出来,你会发现复杂度同上。

(65pts)算法

匹配问题是可以用网络流来解的。
我们可以统计一下每个特产能被哪些人选。
然后???

(100pts)算法

我考场上一直不会维护链上颜色个数,以为需要树上莫队。。。
实际上,因为颜色数只有(1000),开个(bitset)并没有什么问题。
于是可以树链剖分+线段树+(bitset)强行(O(nlog^2n))预处理每个人能选到的特产(dp)
或者说可以预处理出每条链上(bitset)前缀和(用重儿子),这样就只用在(top[u]=top[lca])这一段用线段树查询,复杂度降到(O(nlogn))

然后我们来考虑怎么来求最大的特产数。
注意到每一个人选择的特产的种数是相同的,若每一个人选(x)种特产,则(ans=cx)

(神思路时间)
这相当于每一个人都拆成了(x)个点,然后人和特产之间连边跑完美二分图匹配。
怎么保证完美???
根据霍尔定理,左边的任何一个子集都要满足和右边的相邻的点数大于等于子集大小
右边相邻的点数就是当前状态中包括的人总共能取到的特产数,所以答案很明显就是(min{frac{dp[now]}{|now|})}了。
(now)指当前状态包含的人数。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<bitset>
#define re register
#define il inline
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int mod=1e9+7,N=5e5+100;
struct Edge{int to,nxt;}e[N<<1];
int n,m,Q,a[N],f[N],d[N],top[N],sz[N],c,son[N],ans,h[N],cnt,q[N],id[N],now[N],tim;
il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;}
il ll gi()
{
   re ll x=0,t=1;
   re char ch=getchar();
   while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
   if(ch=='-') t=-1,ch=getchar();
   while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
   return x*t;
}
bitset<1010>dp[50];
bitset<1010>pre[N];
namespace seg_T
{
#define ls (x<<1)
#define rs (x<<1|1)
#define mid ((l+r)>>1)
  bitset<1010>w[N<<2];
  il void Build(re int x,re int l,re int r)
  {
    if(l==r) {w[x][a[q[l]]]=1;return;}
    Build(ls,l,mid);Build(rs,mid+1,r);
    w[x]=w[ls]|w[rs];
  }
  il void Query(re int x,re int l,re int r,re int ql,re int qr,bitset<1010>* res)
  {
    if(ql<=l&&r<=qr) {*res|=w[x];return;}
    if(ql<=mid) Query(ls,l,mid,ql,qr,res);
    if(qr>mid) Query(rs,mid+1,r,ql,qr,res);
  }
}
il void dfs1(re int u,re int fa)
{
  f[u]=fa;d[u]=d[fa]+1;sz[u]=1;pre[u][a[u]]=1;
  for(re int i=h[u];i+1;i=e[i].nxt)
    {
      re int v=e[i].to;
      if(v==fa) continue;
      dfs1(v,u);
      sz[u]+=sz[v];
      if(sz[son[u]]<sz[v]) son[u]=v;
    }
}
il void dfs2(re int u,re int up)
{
  q[id[u]=++tim]=u;
  top[u]=up;
  if(son[u])
    {
      pre[son[u]]|=pre[u];
      dfs2(son[u],up);
    }
  for(re int i=h[u];i+1;i=e[i].nxt)
    {
      re int v=e[i].to;
      if(v==f[u]||v==son[u]) continue;
      dfs2(v,v);
    }
}
il int LCA(re int u,re int v)
{
  while(top[u]^top[v])
    {
      if(d[top[u]]<d[top[v]]) swap(u,v);
      u=f[top[u]];
    }
  return d[u]<d[v]?u:v;
}
il void calc(bitset<1010>* res,re int u,re int v)
{
  while(top[u]^top[v])
    {
      *res|=pre[u];
      u=f[top[u]];
    }
  seg_T::Query(1,1,n,id[v],id[u],res);
}
int main()
{
  freopen("party.in","r",stdin);
  freopen("party.out","w",stdout);
  memset(h,-1,sizeof(h));
  n=gi();m=gi();Q=gi();
  fp(i,2,n)
    {
      re int u=i,v=gi();
      add(v,u);
    }
  fp(i,1,n) a[i]=gi();
  dfs1(1,0);dfs2(1,1);
  seg_T::Build(1,1,n);
  while(Q--)
    {
      c=gi();
      fp(i,0,c-1) now[i]=gi();
      re int lca=now[0];
      fp(i,1,c-1) lca=LCA(lca,now[i]);
      fp(i,0,c-1)
	{
	  dp[(1<<i)].reset();
	  calc(&dp[(1<<i)],now[i],lca);
	}
      ans=m/c;
      fp(i,1,(1<<c)-1)
	{
	  re int x=i,t=0;
	  while(x) {x-=x&-x;++t;}
	  if(t^1)
	    {
	      re int tt=(i-1)&i;
	      dp[i]=dp[tt]|dp[i^tt];
	    }
	  ans=min(ans,(int)dp[i].count()/t);
	}
      printf("%d
",ans*c);
    }
  fclose(stdin);
  fclose(stdout);
  return 0;
}
原文地址:https://www.cnblogs.com/yanshannan/p/9434379.html