Bzoj4899 记忆的轮廓
B. 记忆的轮廓
题目描述
输入格式
输出格式
样例
orz大佬题解:https://blog.csdn.net/WerKeyTom_FTD/article/details/53026266
我这里只是梳理一下自己的思路。
50算法
对于n==p的情况,就是在每一个点都存档,设d[i]表示节点i的儿子数,对于错误节点i,设g[i]为读档的期望步数,则g[i]=1+∑(1/d[i]*g[j]).对于正确节点i,设s[i]=∑g[j](j为i的错误儿子)。设f[i]为从i到n的期望步数,f[n]=0,f[i]=1+f[i+1]/d[i]+∑(g[j]+f[i])/d[i],得f[i]=d[i]+f[i+1]+s[i];
#include<cstdio> #include<iostream> #include<cmath> using namespace std; struct edge { int u,v,next; #define u(x) ed[x].u #define v(x) ed[x].v #define n(x) ed[x].next }ed[200010]; int first[100010],num_e; #define f(x) first[x] int T; int n,m,p; double d[100000],g[100000],s[100000],f[100000]; void dfs(int x,int fa) { //cout<<x<<" "<<fa<<endl; if(x>n)g[x]=1; for(int i=f(x);i;i=n(i)) if(v(i)!=fa) d[x]++; for(int i=f(x);i;i=n(i)) if(v(i)!=fa) { dfs(v(i),x); if(x>n)g[x]+=1/d[x]*g[v(i)]; else s[x]+=g[v(i)]; } if(x<n) f[x]=d[x]+s[x]+f[x+1]; } inline void add(int u,int v); signed main() { cin>>T; while(T--) { cin>>n>>m>>p; for(int i=1;i<n;i++) add(i,i+1),add(i+1,i); int u,v; for(int i=1;i<=m-n;i++) { cin>>u>>v; add(u,v),add(v,u); } if(n==p) { dfs(1,0); printf("%0.4lf\n",f[1]); } } } inline void add(int u,int v) { ++num_e; u(num_e)=u; v(num_e)=v; n(num_e)=f(u); f(u)=num_e; }
70算法
设a[i][j]为当前存档点为i,从i出发到j的期望步数,用n2的复杂度预处理出a,a[i][i]=0,对于i<j,a[i][j]=a[i][j-1]+1+1/d[j-1]*0+∑(g[k]+a[i][j])/d[j-1];(k为j-1的错误儿子),得a[i][j]=a[i][j-1]*d[j-1]+d[j-1]+s[j-1];这里理解了好长时间,我本来写的是a[i][j]=a[i][j-1]+1/d[j-1]+∑(g[k]+a[i][j])/d[j-1];因为从j-1无论如何都要再走一步到他的儿子节点,我是忘了这个……设f[i][j]表示当前存档点为i,还剩j次存档机会,到n的期望步数,
那么f[i][j]=min(f[k][j-1]+a[i][k]),i<k<=n;最后答案为f[1][p-1];复杂度O(n2p);
#include<cstdio> #include<iostream> #include<cmath> #include<cstring> #define ma(x) memset(x,0,sizeof(x)) using namespace std; struct edge { int u,v,next; #define u(x) ed[x].u #define v(x) ed[x].v #define n(x) ed[x].next }ed[20010]; int first[1510],num_e; #define f(x) first[x] int T; int n,m,p; double d[1510],g[1510],s[1510],f[1510]; double a[710][710],f2[710][710]; void dfs(int x,int fa); inline void add(int u,int v); signed main() { //freopen("5.in","r",stdin); cin>>T; while(T--) { num_e=0;ma(d);ma(g);ma(s);ma(f);ma(f2);ma(a);ma(first); cin>>n>>m>>p; for(int i=1;i<n;i++) add(i,i+1),add(i+1,i); int u,v; for(int i=1;i<=m-n;i++) { scanf("%d%d",&u,&v); add(u,v),add(v,u); } dfs(1,0); if(n==p) { printf("%0.4lf\n",f[1]); continue; } for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) a[i][j]=a[i][j-1]*d[j-1]+d[j-1]+s[j-1]; for(int i=n-1;i;i--) for(int j=0;j<=p;j++) f2[i][j]=0x7fffffff; for(int i=n;i>=1;i--) for(int j=1;j<p;j++) for(int k=i+1;k<=n;k++) f2[i][j]=min(f2[i][j] , f2[k][j-1]+a[i][k] ); printf("%0.4lf\n",f2[1][p-1]); } } inline void add(int u,int v) { ++num_e; u(num_e)=u; v(num_e)=v; n(num_e)=f(u); f(u)=num_e; } void dfs(int x,int fa) { if(x>n)g[x]=1; for(int i=f(x);i;i=n(i)) if(v(i)!=fa) { d[x]++; //cout<<i<<" "<<v(i)<<endl; } //cout<<x<<endl; for(int i=f(x);i;i=n(i)) if(v(i)!=fa) { dfs(v(i),x); if(x>n)g[x]+=1/d[x]*g[v(i)]; else s[x]+=g[v(i)]; } if(x<n) f[x]=d[x]+s[x]+f[x+1]; }
70算法+玄学优化AC
题解的分析没有看懂,就是证明了一下a数组不会爆炸的问题,a的增长是非常快的,但答案并不会有那么大,所以可以假定一个常数step,每次转移最多从距离step转移过来,step取40就差不多了,因为a的下界是2^40了,而答案的上界远远没有达到。题解说复杂度是O(np log ans),没有搞懂,O(40np)更容易理解吧,其实也差不多。
#include<cstdio> #include<iostream> #include<cmath> #include<cstring> #define ma(x) memset(x,0,sizeof(x)) using namespace std; struct edge { int u,v,next; #define u(x) ed[x].u #define v(x) ed[x].v #define n(x) ed[x].next }ed[20010]; int first[1510],num_e; #define f(x) first[x] int T; int n,m,p; double d[1510],g[1510],s[1510],f[1510]; double a[710][710],f2[710][710]; const int step=40; void dfs(int x,int fa); inline void add(int u,int v); signed main() { //freopen("5.in","r",stdin); cin>>T; while(T--) { num_e=0;ma(d);ma(g);ma(s);ma(f);ma(f2);ma(a);ma(first); cin>>n>>m>>p; for(int i=1;i<n;i++) add(i,i+1),add(i+1,i); int u,v; for(int i=1;i<=m-n;i++) { scanf("%d%d",&u,&v); add(u,v),add(v,u); } dfs(1,0); if(n==p) { printf("%0.4lf\n",f[1]); continue; } for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) a[i][j]=a[i][j-1]*d[j-1]+d[j-1]+s[j-1]; for(int i=n-1;i;i--) for(int j=0;j<=p;j++) f2[i][j]=0x7fffffff; for(int i=n;i>=1;i--) for(int j=1;j<p;j++) for(int k=i+1;k<=min(i+step,n);k++) f2[i][j]=min(f2[i][j] , f2[k][j-1]+a[i][k] ); printf("%0.4lf\n",f2[1][p-1]); } } inline void add(int u,int v) { ++num_e; u(num_e)=u; v(num_e)=v; n(num_e)=f(u); f(u)=num_e; } void dfs(int x,int fa) { if(x>n)g[x]=1; for(int i=f(x);i;i=n(i)) if(v(i)!=fa) d[x]++; for(int i=f(x);i;i=n(i)) if(v(i)!=fa) { dfs(v(i),x); if(x>n)g[x]+=1/d[x]*g[v(i)]; else s[x]+=g[v(i)]; } if(x<n) f[x]=d[x]+s[x]+f[x+1]; }
100算法
居然是个单队。首先证明a[i][j+1]-a[i][j]>=a[i+1][j+1]-a[i+1][j],把a[i][j+1]展开,得左边=(d[j]-1)*a[i][j]+…,右边=(d[j]-1)*a[i+1][j]+…。得证。则可以用单队优化,复杂度O(np log n)。
另外还有一种做法,目前还没有看懂……