http://codeforces.com/contest/1292/problem/C
注意到编号x的边对答案要有贡献,必须和0到x-1的边一起形成一条链,否则x及编号比x大的边都没有贡献。由此,对答案有贡献的边形成了一条链,且这条链的编号是个谷形,即中间编号小,往两边编号变大,编号最大的边在最外侧。由此可以进行dp,dp[u][v]表示如果上述链为点u到点v这条链的答案。令sz[u][v]为以u为根,子树v的大小;fa[u][v]为以u为根,点v的父亲,则有dp[u][v]=dp[v][u]=sz[u][v]*sz[v][u]+max(dp[u][fa[u][v]],dp[v][fa[v][u]])。(考虑放入编号最大的一条边,相当于某些路径的mex增加了1)
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 5 int n; 6 #define maxn 3011 7 struct Edge{int to,next;}edge[maxn<<1]; int first[maxn],le=2; 8 void in(int x,int y) {Edge &e=edge[le]; e.to=y; e.next=first[x]; first[x]=le++;} 9 void insert(int x,int y) {in(x,y); in(y,x);} 10 11 int sz[maxn][maxn],fa[maxn][maxn]; 12 LL dp[maxn][maxn]; 13 struct NODE{int x,y;}; 14 vector<NODE> vec[maxn]; 15 void dfs(int x,int f,int top,int dep) 16 { 17 sz[top][x]=1; fa[top][x]=f; 18 if (dep>0) vec[dep].push_back((NODE){top,x}); 19 for (int i=first[x];i;i=edge[i].next) 20 { 21 Edge &e=edge[i]; if (e.to==f) continue; 22 dfs(e.to,x,top,dep+1); sz[top][x]+=sz[top][e.to]; 23 } 24 } 25 26 int main() 27 { 28 scanf("%d",&n); 29 for (int i=1,x,y;i<n;i++) {scanf("%d%d",&x,&y); insert(x,y);} 30 for (int i=1;i<=n;i++) dfs(i,0,i,0); 31 32 for (int len=1;len<n;len++) 33 for (int i=0,to=vec[len].size();i<to;i++) 34 { 35 int a=vec[len][i].x,b=vec[len][i].y; 36 dp[a][b]=dp[b][a]=sz[a][b]*sz[b][a]+max(dp[a][fa[a][b]],dp[b][fa[b][a]]); 37 } 38 39 LL ans=0; 40 for (int i=1;i<=n;i++) 41 for (int j=1;j<=n;j++) 42 ans=max(ans,dp[i][j]); 43 printf("%lld\n",ans); 44 return 0; 45 }
http://codeforces.com/contest/1292/problem/D
回忆找重心的过程:从根节点开始,看点权和最大的子树,其点权和是否超过总的一半,是则在该子树中找重心,否则当前点为重心。以1为根时,在深度dep的节点x的子树中的点y必须满足:y的质因子中最大的dep个乘积即为x。由此,从1出发,对所有数进行分解后(相同的合并),每次取每个数的最大的质因子p,算出每个p的点权和,然后看重儿子的点权和是否超过n/2,若否则找到答案,若是则调整答案,并删去该子树中的最大质因子(将其次数-1),然后在该子树中循环进行该过程。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define LL long long 4 5 int n; 6 #define maxn 5011 7 #define MAXNUM 5000 8 int cnt[maxn],fra[maxn][maxn],poi[maxn],cntpri[maxn],pri[maxn],lp; bool vis[maxn]; 9 void makeprime(int n) 10 { 11 for (int i=2;i<=n;i++) 12 { 13 if (!vis[i]) pri[++lp]=i; 14 for (int j=1;j<=lp && 1ll*i*pri[j]<=n;j++) 15 { 16 vis[i*pri[j]]=1; 17 if (i%pri[j]==0) break; 18 } 19 } 20 } 21 22 int main() 23 { 24 scanf("%d",&n); 25 for (int i=1,x;i<=n;i++) scanf("%d",&x),cnt[x]++; 26 makeprime(MAXNUM); 27 28 LL ans=0; 29 for (int x=0;x<=MAXNUM;x++) 30 for (int i=1;i<=lp && pri[i]<=x;i++) 31 { 32 for (int j=pri[i];j<=x;j*=pri[i]) 33 fra[x][i]+=x/j; 34 ans+=1ll*cnt[x]*fra[x][i]; 35 } 36 //trick: The number of prime 2 in k! is k/2+k/4+k/8+... 37 38 for (int x=0;x<=MAXNUM;x++) poi[x]=lp; 39 while (1) 40 { 41 for (int i=1;i<=lp;i++) cntpri[i]=0; 42 for (int x=0;x<=MAXNUM;x++) 43 { 44 while (poi[x]>0 && fra[x][poi[x]]==0) poi[x]--; 45 if (poi[x]>0) cntpri[poi[x]]+=cnt[x]; 46 } 47 bool findans=1; 48 for (int i=1;i<=lp;i++) if (cntpri[i]*2>n) 49 { 50 findans=0; 51 ans-=cntpri[i]-(n-cntpri[i]); 52 for (int x=0;x<=MAXNUM;x++) if (poi[x]>0 && poi[x]==i) fra[x][poi[x]]--; else poi[x]=0; 53 break; 54 } 55 if (findans) break; 56 } 57 printf("%lld\n",ans); 58 return 0; 59 }
。
原文地址:https://www.cnblogs.com/Blue233333/p/12239920.html
时间: 2024-11-06 12:15:03