【XSY2667】摧毀圖狀樹 貪心 堆 DFS序 線段樹
阿新 • • 發佈:2018-03-06
printf con 線段 string def body 暴力 clu while 的。
題目大意
給你一棵有根樹,有\(n\)個點。還有一個參數\(k\)。你每次要刪除一條長度為\(k\)(\(k\)個點)的祖先-後代鏈,問你最少幾次刪完。現在有\(q\)個詢問,每次給你一個\(k\),問你答案是多少。
\(n\leq {10}^5,k\leq {10}^9\)
題解
設\(l\)為這棵樹的葉子個數,顯然當\(k>\)樹的深度時答案都是\(l\)。
下面要證明:答案是\(O(l+\frac{n-l}{k})\)的。
我們從下往上貪心,每次選擇一個未被覆蓋的深度最深的點,覆蓋這個點網上的一條鏈。我們把這些選擇的點稱為關鍵點。把所有關鍵點到父親的連邊斷開。
包含根的那個連通塊和葉子節點的貢獻是\(O(l)\)
對於其他連通塊,顯然深度最深的點到關鍵點的距離是\(k-1\),所以連通塊的大小不小於\(k\)。又因為每個連通塊只有一個關鍵點,所以這部分的貢獻是\(O(\frac{n-l}{k})\)的。
對於每個\(k\),從葉子節點的上一層關鍵點開始暴力做就行了。查詢一個點是否被覆蓋就用DFS序+線段樹(查詢這個點的子樹深度最淺的點深度是多少)。
時間復雜度:\(O(n\log^2 n)\)(調和級數求和再加上數據結構的\(\log\))
代碼
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
#include<queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void sort(int &a,int &b)
{
if(a>b)
swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
char str[100];
sprintf(str,"%s.in",s);
freopen(str,"r",stdin);
sprintf(str,"%s.out",s);
freopen(str,"w",stdout);
#endif
}
int rd()
{
int s=0,c;
while((c=getchar())<'0'||c>'9');
do
{
s=s*10+c-'0';
}
while((c=getchar())>='0'&&c<='9');
return s;
}
void put(int x)
{
if(!x)
{
putchar('0');
return;
}
static int c[20];
int t=0;
while(x)
{
c[++t]=x%10;
x/=10;
}
while(t)
putchar(c[t--]+'0');
}
int upmin(int &a,int b)
{
if(b<a)
{
a=b;
return 1;
}
return 0;
}
int upmax(int &a,int b)
{
if(b>a)
{
a=b;
return 1;
}
return 0;
}
vector<int> g[100010];
int f[100010][20];
int st[100010];
int ed[100010];
int d[100010];
int ti;
int maxd;
int leaves;
int s[100010];
vector<int> c[100010];
void dfs(int x,int fa,int dep)
{
maxd=max(maxd,dep);
f[x][0]=fa;
d[x]=dep;
st[x]=++ti;
int i;
for(i=1;i<=17;i++)
f[x][i]=f[f[x][i-1]][i-1];
int num=0;
s[x]=0x7fffffff;
for(auto v:g[x])
if(v!=fa)
{
dfs(v,x,dep+1);
s[x]=min(s[x],s[v]+1);
num++;
}
if(!num)
{
s[x]=0;
leaves++;
}
ed[x]=ti;
}
int jump(int x,int d)
{
int i;
for(i=17;i>=0;i--)
if(d&(1<<i))
x=f[x][i];
return x;
}
namespace seg
{
int ls[200010];
int rs[200010];
int s[200010];
int rt,n;
void add(int &p,int x,int v,int l,int r)
{
if(!p)
{
p=++n;
ls[p]=rs[p]=0;
s[p]=0x7fffffff;
}
if(l==r)
{
s[p]=v;
return;
}
int mid=(l+r)>>1;
if(x<=mid)
add(ls[p],x,v,l,mid);
else
add(rs[p],x,v,mid+1,r);
s[p]=0x7fffffff;
if(ls[p])
upmin(s[p],s[ls[p]]);
if(rs[p])
upmin(s[p],s[rs[p]]);
}
int query(int p,int L,int R,int l,int r)
{
if(!p)
return 0x7fffffff;
if(L<=l&&R>=r)
return s[p];
int res=0x7fffffff;
int mid=(l+r)>>1;
if(L<=mid)
upmin(res,query(ls[p],L,R,l,mid));
if(R>mid)
upmin(res,query(rs[p],L,R,mid+1,r));
return res;
}
}
struct cmp
{
int operator ()(int a,int b) const
{
return d[a]<d[b];
}
};
priority_queue<int,vector<int>,cmp> q;
int ans[100010];
int k;
int main()
{
open("a");
int n;
scanf("%d",&n);
int i,x,y;
for(i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
dfs(1,0,1);
for(i=1;i<=n;i++)
c[s[i]].push_back(i);
for(i=1;i<maxd;i++)
{
k=i;
int res=leaves;
for(auto v:c[i])
q.push(v);
seg::rt=seg::n=0;
while(!q.empty())
{
x=q.top();
q.pop();
if(s[x]<=k-1)
continue;
if(seg::query(seg::rt,st[x],ed[x],1,n)<=d[x]+k-1)
continue;
seg::add(seg::rt,st[x],d[x],1,n);
res++;
if(d[x]>k)
q.push(jump(x,k));
}
ans[i]=res;
}
int q;
scanf("%d",&q);
while(q--)
{
scanf("%d",&x);
if(x>=maxd)
printf("%d\n",leaves);
else
printf("%d\n",ans[x]);
}
return 0;
}
【XSY2667】摧毀圖狀樹 貪心 堆 DFS序 線段樹