Codeforces Round #507 (Div. 1) D. You Are Given a Tree 根号分治 + dp
傳送門
題意:
有一顆nnn個節點的樹,其中一個簡單路徑集合被稱為kkk合法當且僅當:
樹的每個節點至多屬于一條路徑,且每條路徑恰好包含kkk個點。
對于k∈[1,n]k\in [1,n]k∈[1,n],求kkk合法路徑集合最多路徑個數,即設kkk合法路徑集合為SSS,求最大的∣S∣|S|∣S∣。
2≤n≤1e52\le n\le 1e52≤n≤1e5
思路:
考慮每次用dpdpdp來O(n)O(n)O(n)來求,記一個最大值和次大值,讓后就是比較常規的dpdpdp了,這樣的復雜度是O(n2)O(n^2)O(n2)的。
考慮到當k∈[n,n]k\in [\sqrt n,n]k∈[n?,n]的時候,答案不會超過n\sqrt nn?個,也就是每個答案有可能很長一段連續的都是這個答案,且答案遞減,所以這個東西有二分的性質。
考慮根號分治,對于k∈[1,n]k\in [1,\sqrt n]k∈[1,n?]的情況,我們直接暴力求,復雜度O(nn)O(n\sqrt n)O(nn?)。對于k∈[n,n]k\in [\sqrt n,n]k∈[n?,n],我們每次二分答案所屬區間,復雜度O(nnlogn)O(n\sqrt n logn)O(nn?logn)。
直接寫會ttt掉,畢竟這個復雜度還是很高的,所以考慮將dpdpdp的dfsdfsdfs拿出來dfsdfsdfs序,循環跑一下,這樣會快很多,可以通過。
// Problem: D. You Are Given a Tree // Contest: Codeforces - Codeforces Round #507 (Div. 1, based on Olympiad of Metropolises) // URL: https://codeforces.com/problemset/problem/1039/D // Memory Limit: 512 MB // Time Limit: 7000 ms // // Powered by CP Editor (https://cpeditor.org)//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math") //#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native") //#pragma GCC optimize(2) #include<cstdio> #include<iostream> #include<string> #include<cstring> #include<map> #include<cmath> #include<cctype> #include<vector> #include<set> #include<queue> #include<algorithm> #include<sstream> #include<ctime> #include<cstdlib> #include<random> #include<cassert> #define X first #define Y second #define L (u<<1) #define R (u<<1|1) #define pb push_back #define mk make_pair #define Mid ((tr[u].l+tr[u].r)>>1) #define Len(u) (tr[u].r-tr[u].l+1) #define random(a,b) ((a)+rand()%((b)-(a)+1)) #define db puts("---") using namespace std;//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); } //void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); } //void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }typedef long long LL; typedef unsigned long long ULL; typedef pair<int,int> PII;const int N=100010,mod=1e9+7,INF=0x3f3f3f3f; const double eps=1e-6;int n; int ans[N],block; vector<int>v[N]; int dfn[N],tot,fa[N],id[N];void dfs(int u,int f) {int mx1=0,mx2=0;int now=0;fa[u]=f;dfn[u]=++tot;for(auto x:v[u]) {if(x==f) continue;dfs(x,u);// now+=y;// if(mx1<len[x]) mx2=mx1,mx1=len[x];// else if(mx2<len[x]) mx2=len[x];}// if(mx1+mx2+1>=k) now++,len[u]=0;// else len[u]=mx1+1;// return now; }int f[N],mx1[N],mx2[N]; int len[N];int solve(int k) {if(ans[k]!=-1) return ans[k];for(int i=0;i<=n;i++) {mx1[i]=mx2[i]=0;f[i]=0;len[i]=0;}for(int i=1;i<=n;i++) {int now=id[i];int to=fa[now];if(mx1[now]+mx2[now]+1>=k) {f[now]++; len[now]=0;} else len[now]=mx1[now]+1;f[to]+=f[now];if(mx1[to]<len[now]) {mx2[to]=mx1[to];mx1[to]=len[now];} else if(mx2[to]<len[now]) {mx2[to]=len[now];}}return ans[0]=f[0]; }bool cmp(int a,int b) {return dfn[a]>dfn[b]; } int main() { // ios::sync_with_stdio(false); // cin.tie(0);memset(ans,-1,sizeof(ans));scanf("%d",&n); block=sqrt(n*log(n)/log(2));for(int i=1;i<=n-1;i++) {int a,b; scanf("%d%d",&a,&b);v[a].pb(b); v[b].pb(a);}dfs(1,0);for(int i=1;i<=n;i++) ans[i]=-1,id[i]=i;sort(id+1,id+1+n,cmp);for(int i=1;i<=block;i++) {ans[i]=solve(i);}int pre=block+1;for(int _=1;pre<=n;_++) {int l=pre,r=n,ne,val;val=solve(l);while(l<=r) {int mid=(l+r)>>1;if(solve(mid)<val) r=mid-1;else l=mid+1,ne=mid;}for(int i=pre;i<=ne;i++) ans[i]=solve(pre);pre=ne+1; }for(int i=1;i<=n;i++) printf("%d\n",ans[i]);return 0; } /**/總結
以上是生活随笔為你收集整理的Codeforces Round #507 (Div. 1) D. You Are Given a Tree 根号分治 + dp的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 飞鱼星路由器的设置是什么飞鱼星双wan口
- 下一篇: 2020EC-final