JZOJ5944信标
emmm樹形dp?好像是的
搬一個題解證明過來
由于在n>1時答案至少為1,我們枚舉一個必須放的根, 所有深度不同的點就被區分開了.
設一個節點有c個兒子, 發現必須在其中至少c?1個兒子的子樹中放置信標.
證明如下: 考慮如果不這樣放, 對于兩棵都沒有放的子樹, 他們匯集到lca上以后距離都是相等的, 所以lca外的信標無法區分, 而內部沒有信標. 所以不能存在兩顆子樹都不放. 所以至少要放c-1個. 由于在根節點放置了信標, 可以只考慮深度相同的點. 由于深度相同, 所以他們的lca度數至少為2,那么一定有一個信標在lca包含這兩個點的兩支子樹中. 那么另一側的點肯定要走更遠的路, 會被區分開. 所以放c?1個足夠區分.
這樣問題變成每個節點要有c?1棵子樹放有信標, 求最小方案. 直接貪心即可.
由于枚舉根所 以復雜度為O(n2), 可以獲得70分.
如何做到O(n)?
我們先特判鏈的情況答案為1,然后找到任意一個度數大于2的節點,可以證明這個點一定不需要放置信標. 于是以這個點作根O(n)的貪心即可.
證明如下:
深度相同的點對證明同上,只考慮深度不同的點對.如果它們在一顆子樹中,由于度數大于2所以一定有另一顆子樹的一個信標把他們區分開.
如果在不同的子樹中, 有兩種情況:
一個在沒放信標的子樹中,一個在放了的子樹中.顯然還存在另一個子樹放了信標,由于深度不同他們會被這個信標區分開.
兩個都在放了信標的子樹中. 如果根的度數大于3則同上. 度數等于3時, 如果他們沒有被區分開,一定是他們先匯集到了一個節點上, 然后走到同一個信標上. 這個點一定是一條奇鏈的中點, 且 不是根 (由于深度不同), 是在兩個子樹之一中唯一的. 那么他們走到另一個信標就一定有一個點走 了冤枉路, 既另一個信標可以區分出他們.
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define rint register int template <class T>inline void read(T &X) {X=0;int W=0;char ch=0;while(!isdigit(ch))W|=ch=='-',ch=getchar();while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();X=W?-X:X;return; } int n,head[1000010],cnt=0,count[1000010],f[1000010]; struct node{int to,next;}edge[2000010];void add(int u,int v) {edge[++cnt].to=v;edge[cnt].next=head[u];head[u]=cnt; }void dfs(int now,int fa) {int num=0,tot=0;for(rint i=head[now];i;i=edge[i].next){int to=edge[i].to;if(to==fa)continue;dfs(to,now);tot+=f[to];if(!f[to])num++;}f[now]=tot;if(num>1)f[now]+=(num-1);return; } int main() {//freopen("beacon.in","r",stdin);//freopen("beacon.out","w",stdout); read(n);if(n==1){printf("0\n");return 0;}for(rint i=1;i<n;++i){int u,v;read(u),read(v);add(u,v),add(v,u);++count[u],++count[v];}int sta=-1;for(rint i=1;i<=n;++i)if(count[i]>=3)sta=i;if(sta==-1){printf("1\n");return 0;}dfs(sta,-1);printf("%d\n",f[sta]); return 0; }?
轉載于:https://www.cnblogs.com/pile8852/p/9892325.html
總結
以上是生活随笔為你收集整理的JZOJ5944信标的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java课程课后作业04之动手动脑
- 下一篇: 搜集的一些有意思的牛人博客地址