其他分享
首页 > 其他分享> > [luogu8341]回忆

[luogu8341]回忆

作者:互联网

考虑将所有极深的$t$配对,表示选择对应的路径(要求经过$1$)

具体的,假设$1$的儿子子树内分别有$a_{1},a_{2},...,a_{k}$个$t$,对其分类讨论:

1.若$2\max a_{i}\le \sum a_{i}$,则可以配成$\lceil\frac{\sum a_{i}}{2}\rceil$对(归纳证明),且取到答案下限

2.若$2\max a_{i}>\sum a_{i}$,则可以配成$\sum a_{i}-\max a_{i}$对,且仅需考虑$\max a_{i}$对应子树的子问题

关于这个子问题,与原问题相比有以下不同:

1.可以"免费"选择$\sum a_{i}-\max a_{i}$条"直路径"(端点构成祖先-后代关系)

2.需处理$s_{i}=1$且$t_{i}$在该子树内的限制,具体如下——

(1)若其子树内存在标记,则该限制已经满足,跳过即可

(2)若其某个祖先存在标记,取该祖先(必然唯一),将该标记删除

(3)若存在"免费"的"直路径",则将其数量减1,否则将答案加1

(4)对于第(2)和(3)种,在$t_{i}$上打一个标记,表示可以"免费"选择经过$t_{i}$的"直路径"

在此基础上,需要将$a_{i}$减去子树内标记数,判定条件中将$\max a_{i}$和$\sum a_{i}$均减去1.的数量

可以使用树状数组维护(找祖先求和即可),时间复杂度为$o(n\log n)$,可以通过

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define N 200005
  4 int t,n,m,x,y,tag,ans,dfn[N],sz[N],a[N],f[N],sum[N],cnt[N],Tag[N];
  5 vector<int>e[N],v[N];
  6 int read(){
  7     int x=0;char c=getchar();
  8     while ((c<'0')||(c>'9'))c=getchar();
  9     while ((c>='0')&&(c<='9'))x=x*10+c-'0',c=getchar();
 10     return x;
 11 }
 12 int lowbit(int k){
 13     return (k&(-k));
 14 }
 15 void update(int k,int x){
 16     while (k<=n)f[k]+=x,k+=lowbit(k);
 17 }
 18 int query(int k){
 19     int ans=0;
 20     while (k)ans+=f[k],k^=lowbit(k);
 21     return ans;
 22 }
 23 void Update(int k,int x){
 24     while (k<=n)sum[k]+=x,k+=lowbit(k);
 25 }
 26 int Query(int k){
 27     int ans=0;
 28     while (k)ans+=sum[k],k^=lowbit(k);
 29     return ans;
 30 }
 31 void add(int k){
 32     Tag[k]++,update(dfn[k],1);
 33     Update(dfn[k],k),Update(dfn[k]+sz[k],-k);
 34 }
 35 void dec(int k){
 36     Tag[k]--,update(dfn[k],-1);
 37     Update(dfn[k],-k),Update(dfn[k]+sz[k],k);
 38 }
 39 bool cmp(int x,int y){
 40     return dfn[x]<dfn[y];
 41 }
 42 void dfs(int k,int fa){
 43     dfn[k]=++dfn[0],sz[k]=1;
 44     for(int i:e[k])
 45         if (i!=fa)dfs(i,k),sz[k]+=sz[i];
 46 }
 47 void get_cnt(int k,int fa){
 48     for(int i:e[k])
 49         if (i!=fa)get_cnt(i,k),cnt[k]+=cnt[i];
 50 }
 51 void calc(int k,int fa){
 52     if (Tag[k])tag++,dec(k);
 53     int s=0,mx=0,son;
 54     for(int i:e[k])
 55         if (i!=fa){
 56             a[i]=cnt[i]-(query(dfn[i]+sz[i]-1)-query(dfn[i]-1));
 57             s+=a[i],mx=max(mx,a[i]);
 58         }
 59     if (s<=tag)return; 
 60     if ((mx-tag<<1)<=s-tag){
 61         ans+=(s-tag+1>>1);
 62         return;
 63     }
 64     tag+=s-mx,ans+=s-mx;
 65     for(int i:e[k])
 66         if ((i!=fa)&&(a[i]==mx)){
 67             son=i;
 68             break;
 69         }
 70     for(int i:v[k])
 71         if ((dfn[son]<=dfn[i])&&(dfn[i]<dfn[son]+sz[son])){
 72             if (query(dfn[i]-1)<query(dfn[i]+sz[i]-1))continue;
 73             int pos=Query(dfn[i]);
 74             if (pos)dec(pos);
 75             else{
 76                 if (tag)tag--;else ans++;
 77             }
 78             add(i);
 79         }
 80     calc(son,k); 
 81 }
 82 int main(){
 83     t=read();
 84     while (t--){
 85         n=read(),m=read();
 86         tag=ans=dfn[0]=0;
 87         for(int i=1;i<=n;i++){
 88             f[i]=sum[i]=cnt[i]=Tag[i]=0;
 89             e[i].clear(),v[i].clear();
 90         }
 91         for(int i=1;i<n;i++){
 92             x=read(),y=read();
 93             e[x].push_back(y),e[y].push_back(x);
 94         }
 95         for(int i=1;i<=m;i++){
 96             x=read(),y=read();
 97             a[i]=y,v[x].push_back(y);
 98         }
 99         dfs(1,0),sort(a+1,a+m+1,cmp);
100         for(int i=m;i;i--){
101             int k=a[i];
102             if (query(dfn[k]-1)==query(dfn[k]+sz[k]-1))cnt[k]=1;
103             update(dfn[k],1);
104         }
105         for(int i=1;i<=n;i++)f[i]=0;
106         get_cnt(1,0),calc(1,0),printf("%d\n",ans);
107     }
108     return 0;
109 }
View Code

 

标签:标记,int,max,sum,路径,luogu8341,mx,回忆
来源: https://www.cnblogs.com/PYWBKTDA/p/16287156.html