爆零蒟蒻欢乐多......

T1:

北京集训:20180320
怎么又是构造题啊......
算了扔了不看了......
事实证明这种选择似乎是正确的......
我们直接来看正解:

北京集训:20180320
考虑为什么我们把不同子树的叶子节点连接起来是正确的。
考虑我们割掉一个点,如果他是叶子节点,则原图显然还是联通的。
而如果他不是叶子节点,则他的子树可以通过那些叶子节点连的边通过根节点的其他子树与根节点互连。
所以这个东西显然是正确的,而题解取的max,是因为我们每个叶子节点都必须要连边,
而对于每一个点,分开他形成的叶子节点的子树们都需要互连。
考虑怎么构造,我们先找到重心,并dfs出以他为根的各个子树中的叶子节点。
然后我们钦定一个节点作为hub,其余节点两两配对,如果无法配对则与hub连接。
而为了避免同一个子树内的节点不得不相连(显然这样不优),我们需要把这些子树按照大小排序,连边的时候按照一个从大到小启发式的顺序连接。
为什么,因为重心的每一个子树中的叶子节点个数一定不大于(fulleaf+1)/2。(否则我们可以调整重心位置是吧)
正解代码:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<vector>
  6 #include<queue>
  7 #define debug cout
  8 using namespace std;
  9 const int maxn=5e5+1e2;
 10 
 11 int s[maxn],t[maxn<<1],nxt[maxn<<1],deg[maxn],cnt;
 12 int mx[maxn],sizleaf[maxn],full;
 13 vector<int> leafs[maxn];
 14 priority_queue<pair<unsigned,int> > pq;
 15 vector<pair<int,int> > ans;
 16 int n;
 17 
 18 inline void addedge(int from,int to) {
 19     ++deg[to];
 20     t[++cnt] = to , nxt[cnt] = s[from] , s[from] = cnt;
 21 }
 22 inline void pre(int pos,int fa) {
 23     mx[pos] = sizleaf[pos] = ( deg[pos] == 1 );
 24     for(int at=s[pos];at;at=nxt[at]) if( t[at] != fa ) {
 25         pre(t[at],pos) , sizleaf[pos] += sizleaf[t[at]] , 
 26         mx[pos] = max(mx[pos],sizleaf[t[at]]);
 27     }
 28 }
 29 inline void dfs(int pos,int fa) {
 30     if( deg[pos] == 1 ) return leafs[full].push_back(pos);
 31     for(int at=s[pos];at;at=nxt[at]) if( t[at] != fa ) dfs(t[at],pos);
 32 }
 33 
 34 inline bool cmp(const vector<int> &a,const vector<int> &b) {
 35     return a.size() > b.size();
 36 }
 37 inline void repush(int p) {
 38     leafs[p].pop_back();
 39     if( leafs[p].size() ) pq.push(make_pair(leafs[p].size(),p));
 40 }
 41 inline void solve_case() {
 42     pre(1,-1);
 43     const int fulleaf = sizleaf[1];
 44     int cent = 1;
 45     for(int i=1;i<=n;i++) {
 46         mx[i] = max( mx[i] , fulleaf - sizleaf[i] );
 47         if( mx[i] < mx[cent] ) cent = i;
 48     }
 49     for(int at=s[cent];at;at=nxt[at]) {
 50         ++full;
 51         dfs(t[at],cent);
 52     }
 53     sort(leafs+1,leafs+1+full,cmp);
 54     pq.push(make_pair(leafs[1].size(),1));
 55     const int tp = leafs[1].back();
 56     for(int i=2;i<=full;i++)
 57         if( leafs[i].size() ) {
 58             if( pq.empty() ) {
 59                 ans.push_back(make_pair(leafs[i].back(),tp));
 60             } else {
 61                 const int j = pq.top().second; pq.pop();
 62                 ans.push_back(make_pair(leafs[i].back(),leafs[j].back()));
 63                 repush(j);
 64             }
 65             repush(i);
 66         }
 67     while( pq.size() ) {
 68         const int i = pq.top().second; pq.pop();
 69         if( !pq.size() ) {
 70             ans.push_back(make_pair(leafs[i].back(),tp));
 71         } else {
 72             const int j = pq.top().second; pq.pop();
 73             ans.push_back(make_pair(leafs[i].back(),leafs[j].back()));
 74             repush(j);
 75         }
 76         repush(i);
 77     }
 78     printf("%u\n",(unsigned)ans.size());
 79     for(unsigned i=0;i<ans.size();i++) printf("%d %d\n",ans[i].first,ans[i].second);
 80 }
 81 
 82 inline void reset() {
 83     #define mem(x) memset(x,0,sizeof(*x)*(n+1))
 84     mem(s) , mem(deg) , mem(mx) , mem(sizleaf);
 85     cnt = full = 0 , ans.clear();
 86     for(int i=0;i<=n;i++) leafs[i].clear();
 87     while( pq.size() ) pq.pop();
 88 }
 89 
 90 int main() {
 91     static int T;
 92     scanf("%d",&T);
 93     while(T--) {
 94         scanf("%d",&n) , reset();
 95         for(int i=1,a,b;i<n;i++) {
 96             scanf("%d%d",&a,&b) ,
 97             addedge(a,b) , addedge(b,a);
 98         }
 99         solve_case();
100     }
101     return 0;
102 }
View Code

相关文章: