周赛作业,Nastia Plays with a Tree(直链剖分)
Disclaimer: 这题确实不是我自己做出来的,是wa了n发无果之后看题解,不过彻底整明白了,所以来偷懒写点东西
这道题就是说把一棵树变成链,所需要的最少操作次数,输出最后要删的边和要加的边
其实我刚开始想的是,从节点1开始,然后向下从第二层打top标记,然后选出来其中最长的两个链(如果存在第二条链),然后把两条最长链以外的边都删掉,加到链上去
后来wa了,我发现个问题,样例给的是一棵基本平衡的树,但是因为这是一棵无根树,所以一旦树不是平衡的,就没办法了,这样的话还需要找树的中心,甚烦
然后就随机算法吧,结果发现这个题早就预料到了这种情况,做两次最长链会t2
然后我就没辙了
后来发现这题其实就是贪心就好了,所有符合in degree == 1的点,如果in degree > 1,那么我们就可以删掉多余的边,第一遍记录删的边,然后根据链的性质,将所有的图上的点分为直链和孤点两种结构
最后我们第二次dfs记录下每个链的头和尾,如果是孤点,那么头 = 尾,然后再把删掉的边给连上去就行了
#include <bits/stdc++.h> using namespace std; constexpr int limit = (300000 + 5);//防止溢出 #define INF 0x3f3f3f3f #define inf 0x3f3f3f3f3f #define lowbit(i) i&(-i)//一步两步 #define EPS 1e-9 #define FASTIO ios::sync_with_stdio(false);cin.tie(0),cout.tie(0); #define ff(a) printf("%d\n",a ); #define pi(a, b) pair<a,b> #define rep(i, a, b) for(ll i = a; i <= b ; ++i) #define per(i, a, b) for(ll i = b ; i >= a ; --i) #define MOD 998244353 #define traverse(u) for(int i = head[u]; ~i ; i = edge[i].next) #define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\data.txt", "rt", stdin) #define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\akioi\\dabiao.txt", "wt", stdout) typedef long long ll; typedef unsigned long long ull; char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf; inline ll read() { #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) ll sign = 1, x = 0; char s = getchar(); while (s > '9' || s < '0') { if (s == '-')sign = -1; s = getchar(); } while (s >= '0' && s <= '9') { ll x = (x << 3) + (x << 1) + s - '0'; s = getchar(); } return x * sign; #undef getchar }//快读 void print(ll x) { if (x / 10) print(x / 10); *O++ = x % 10 + '0'; } void write(ll x, char c = 't') { if (x < 0)putchar('-'), x = -x; print(x); if (!isalpha(c))*O++ = c; fwrite(obuf, O - obuf, 1, stdout); O = obuf; } int n, m, k; int a[limit]; vector<int>g[limit], g2[limit]; void add(int u, int v){ g[u].push_back(v); } int vis[limit]; int deg[limit]; vector<pi(int, int)>gg; void dfs(int u, int pre){ int tot = 0; for(auto & v : g[u]){ if(v == pre)continue; dfs(v, u); } for(auto && v : g[u]){ if(v == pre)continue; if(tot < 2 and deg[v] <= 2){ ++tot; g2[u].push_back(v); g2[v].push_back(u); }else{ deg[u]--, deg[v]--; gg.push_back({u, v}); } } } int appender; int b[limit]; void dfs2(int u, int pre, int id){ vis[u] = 1; if(deg[u] == 1){ if(pre - -1){ b[id] = u; }else{ a[id] = u; } } for(auto && v : g2[u]){ if(v != pre){ dfs2(v, u, id); } } }; void solve(){ cin>>n; gg.clear(); rep(i,1,n)g[i].clear(),g2[i].clear(), deg[i] = 0, vis[i] = 0; rep(i, 1, n - 1){ int x, y; cin>>x>>y; add(x, y); add(y, x); deg[x]++,deg[y]++; } dfs(1, 0); cout<<gg.size()<<endl; int tot = 0; rep(i,1,n){ if(!deg[i]){ a[tot] = i; b[tot++] = i; }else if(deg[i] == 1 and !vis[i]){ dfs2(i, -1, tot++); } } rep(i,0, (int)gg.size() - 1){ cout<<gg[i].first<<" "<<gg[i].second<<" "<<a[i]<<" "<<b[i + 1]<<endl; } }; int32_t main() { #ifdef LOCAL FOPEN; // FOUT; #endif FASTIO int kase; cin>>kase; while (kase--) solve(); cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << "s\n"; return 0; }
天才选手zerol的主页:https://zerol.me/
|
WeepingDemon的个人主页:https://weepingdemon.gitee.io/blog/