这一场比上一场还吃屎 只back回三道题 第四题表示被吓尿不会back
Sonya and Problem Wihtout a Legend
第一题就出了codeforce #371 div1 的第三题我去 比赛的时候一看到这种题压根就是跳 肯定是什么想法题 但是的话这道题的想法好像后来听题解没这么重
就是把严格上升改为不上升 然后的话这些数记录下来 因为以后不管哪个数变都会变成这些数 理由很简单 在中间的不用变 在上面的变下来 在下面的变上去 不用变多不用变少
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<cstdlib> #include<cmath> #define Maxn 3010 using namespace std; typedef long long LL; LL A[Maxn]; LL N; LL B[Maxn]; LL F[Maxn][Maxn]; int main() { //freopen("a.in","r",stdin); //freopen("a.out","w",stdout); scanf("%I64d",&N); for(LL i=1;i<=N;i++) scanf("%I64d",&A[i]),A[i]-=i,B[i]=A[i]; sort(B+1,B+N+1); LL len=unique(B+1,B+N+1)-(B+1); memset(F,63,sizeof(F)); for(LL i=1;i<=len;i++) F[1][i]=abs(A[1]-B[i]),F[1][i]=min(F[1][i],F[1][i-1]); for(LL i=2;i<=N;i++) { for(LL j=1;j<=len;j++) { F[i][j]=min(F[i][j],F[i-1][j]+abs(A[i]-B[j])); F[i][j]=min(F[i][j],F[i][j-1]); } } return printf("%I64d ",F[N][len]),0; } /* 7 2 1 5 11 5 9 11 */
第二题是contest hunter上CH round #50的第三题 这里顺便说一句 ljm的比赛来源好广啊 被虐的好惨啊
这道题感觉很厉害 但听完题解后感觉题解的tree dp有点玄学
F1[x][i] 表示这个点的管辖者在子树下面且距离到i
F2[x][i] 表示这个点的管辖者在子树上面且距离到i
G[x]=min(F1[x][]);
然后转移就是:
F1[x][i]=min(F1[son[x]][i-1]+C[i]+sigma(G[otherson[x]],F2[otherson[x]][i+1]))
什么意思呢 就是因为是下面管我的 所以我要找一个孩子使得它子树下面管我 然后其他孩子要不是下面管 要不是上面管,反正我只要这个子树的花费
F2[x][i]=C[i]+sigma(min(F2[son[x]][i+1],G[son[x]])
这个的意思是现在我是上面管的 只要加上我现在的花费 然后下面子树的花费也传上来就好了
但是有个问题就是 如果F1[x][i]是在下面有i管 但是实际上是由另外一个管怎么办呢 其实没关系 因为另外一个管的自然会有一个更好的状态
然后还要优化一下F1的来做 自己建的时候F1就直接把下面的传上来就好了 加上费用 F2也是如此
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cstdlib> #define Maxn 200010 using namespace std; const int inf=1e9; int N,C[Maxn]; struct node{int x,y,next;}edge[Maxn*2]; int len,first[Maxn]; void ins(int x,int y){len++; edge[len].x=x; edge[len].y=y; edge[len].next=first[x]; first[x]=len;} int prime[Maxn],pri,phi[Maxn]; bool v[Maxn]; void PHI() { memset(v,1,sizeof(v)); v[0]=v[1]=0; pri=0; for(int i=2;i<=N;i++) { if(v[i]){prime[++pri]=i; phi[i]=i-1;} for(int j=1;(j<=pri)&&(prime[j]*i<=N);j++) { v[i*prime[j]]=0; if(i%prime[j]) phi[i*prime[j]]=phi[i]*phi[prime[j]]; else{phi[i*prime[j]]=phi[i]*prime[j]; break;} } } } int T[Maxn][25]; int F1[Maxn][25]; int F2[Maxn][25]; int G[Maxn]; int sum[Maxn]; void Dfs(int x,int fa) { if(first[x]==-1) { F1[x][0]=F2[x][0]=G[x]=C[0]; for(int i=1;i<=19;i++) F2[x][i]=C[i]; return ; } for(int k=first[x];k!=-1;k=edge[k].next) { int y=edge[k].y; if(y!=fa) Dfs(y,x); } for(int i=0;i<=19;i++) sum[i]=0; for(int d=0;d<=19;d++) { for(int k=first[x];k!=-1;k=edge[k].next) { int y=edge[k].y; if(y!=fa) {T[y][d]=min(G[y],F2[y][d+1]); sum[d]+=T[y][d];} } } for(int d=1;d<=19;d++) { for(int k=first[x];k!=-1;k=edge[k].next) { int y=edge[k].y; if(y!=fa) F1[x][d]=min(F1[x][d],C[d]+F1[y][d-1]+sum[d]-T[y][d]); } }F1[x][0]=min(F1[x][0],C[0]+sum[0]); for(int d=0;d<=19;d++) { for(int k=first[x];k!=-1;k=edge[k].next) { int y=edge[k].y; if(y!=fa) F2[x][d]=min(F2[x][d],C[d]+sum[d]); } } for(int i=0;i<=19;i++) G[x]=min(G[x],F1[x][i]); } int main() { scanf("%d%d",&N,&C[0]); for(int i=1;i<N;i++) scanf("%d",&C[i]); PHI(); len=0; memset(first,-1,sizeof(first)); for(int i=2;i<=N;i++) ins(phi[i],i); memset(F1,63,sizeof(F1)); memset(F2,63,sizeof(F2)); memset(G,63,sizeof(G)); memset(T,63,sizeof(T)); Dfs(1,0); return printf("%d ",G[1]),0; } /* 3 3 1 2 */
这道题贼6 bzoj三倍经验
首先我们考虑选一个点的情况 肯定要选重心 而重心就是所有子节点的子树到达重心的最大的花费最小(到后面可以简单的证明一下)
然后的话我们考虑两个点的情况 两个点他们所管辖的区域一定是一个联通块 这个很显然
那么两个联通块之间 有且只有一条边相连 所以的话我们枚举每一条边 在两个端点的左右边分别找重心
当然树这么大怎么找 我们其实可以知道 重心只会在重链上 与层数有关
对于还要乘于个距离 我们先求出以1为根的所有乘上距离的花费ans(怎么做很简单吧)
然后我们考虑砍掉一条边 当然ans要减去砍完这条边失去了多少费用
然后目前的费用就是砍完后两边树的费用 分别以y和1为根的树
找重心我以1为根的树为例 sum[i]为i下面所有的权值和 (没乘花费)
那么的话转移就是 x->y ans-=sum[y]; 表示y子树下面的距离都要减1 然后ans+=sum[1]-sum[y] 表示y上面的子树的距离都加1
然后一切就说通了 合起来就是ans+=sum[1]-2*sum[y] 所以的话最重的孩子sum[y](最重的孩子也就是sum最大的)如果大于的话sum[1]才能转移 不然的话就比最少花费要多
所以整一棵树我们不用扫一遍然后来维护所用权值乘上边权 只要维护权值就好了 乘上的边权在转移树的重心的时候搞一下就好了
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<cstdlib> #include<cmath> #include<climits> #define Maxn 50010 using namespace std; typedef long long LL; struct node { LL x,y,next; }edge[Maxn*2]; LL len,first[Maxn]; void ins(LL x,LL y){len++; edge[len].x=x; edge[len].y=y; edge[len].next=first[x]; first[x]=len;} LL N; LL son1[Maxn],son2[Maxn]; LL size[Maxn]; LL Val[Maxn]; LL dep[Maxn]; LL fa[Maxn]; void Dfs(LL x,LL f) { size[x]+=Val[x]; for(LL k=first[x];k!=-1;k=edge[k].next) { LL y=edge[k].y; if(y!=f) { dep[y]=dep[x]+1; fa[y]=x; Dfs(y,x); size[x]+=size[y]; if(size[son1[x]]<size[y]) son2[x]=son1[x],son1[x]=y; else if(size[son2[x]]<size[y]) son2[x]=y; } } } int main() { len=0; memset(first,-1,sizeof(first)); scanf("%lld",&N); for(LL i=1;i<N;i++){LL x,y; scanf("%lld%lld",&x,&y); ins(x,y); ins(y,x);} for(LL i=1;i<=N;i++) scanf("%lld",&Val[i]); Dfs(1,0); LL S=0; for(LL i=2;i<=N;i++) S+=size[i]; LL Minx=LLONG_MAX; for(LL k=1;k<=len;k+=2) { LL x=edge[k].x; LL y=edge[k].y; if(dep[x]<dep[y]) swap(x,y); LL sum=S; LL u; LL v; for(u=fa[x];u;u=fa[u]) size[u]-=size[x],sum-=size[x]; u=1; while(1) { if(size[son1[u]]>size[son2[u]]&&son1[u]!=x) v=son1[u]; else v=son2[u]; if(size[v]*2>size[1]){sum+=size[1]-2*size[v]; u=v;} else break; } u=x; while(1) { v=son1[u]; if(size[v]*2>size[x]){sum+=size[x]-2*size[v]; u=v;} else break; } Minx=min(Minx,sum); for(u=fa[x];u;u=fa[u]) size[u]+=size[x]; } return printf("%lld ",Minx),0; } /* 5 1 2 1 3 3 4 3 5 5 7 6 5 4 N<=50000 W<=100 */