zoukankan      html  css  js  c++  java
  • [考试反思]0416省选模拟72:停滞

    大约是来搞笑的了。

    $T1$想到了$wqs$二分但是不会(或者说根本没往这个方向想)$dp$。

    $T2$神仙题暂且不管($15$是全场最高但是有个啥用?)

    $T3$的话会$30pts$的状压,但是觉得对我整场的分数并没有什么作用于是也就没有写。随便输出了个$0$拿走$10pts$

    主观来讲,我大概是没长脑子,啥都想不出来

    客观来讲,题有一点不对胃口,好像考试的时候始终不太敢去猜测性质然后就开始写代码

    那些用的不多的知识点以及扩展出来的性质就更不敢用了,可能也是导致这样的原因

    大约多做题是一个合理的解决办法,至少下次再遇到这些东西,我不会慌了。。。

    又把锅甩给了下次啊。。。

    T1:新访问计划

    大意:边权树,要求遍历每条树边至少一次最后回到根,可以花费$c$的代价直接传送到任意点最多$k$次。求最小代价。多测。$sum n,k le 10^5,n le 2 imes 10^4,w_i,c le c imes 10^4$

    可以转化题意:首先要求回路那么所有树边必须走一次,接下来你可以用树边 和 代价为$c$的树上路径至多$k$条 来覆盖整棵树。求最小代价。

    可以搞出一个$dp$表示$f[i][j][0/1]$表示是否有一个未配对(可以再次转弯的)路径连向当前$i$且子树内完全被覆盖而已经申请了$j$条路径的最小代价。

    子树归并。$0/1 +  0/1 ightarrow 0/1$共$8$种情况其中有一种不合法剩下的按照含义转移即可。

    复杂度是$O(n^2)$的。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define S 2222
     4 #define inf 1000000000
     5 int f[S][S],g[S][S],n,fir[S],l[S<<1],to[S<<1],w[S<<1],ec,k,c,sz[S],F[S],G[S],tot;
     6 void link(int a,int b,int x){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;w[ec]=x;}
     7 void con(int a,int b,int x){link(a,b,x);link(b,a,x);}
     8 void dfs(int p,int fa){
     9     sz[p]=1; f[p][1]=g[p][0]=0; f[p][0]=g[p][1]=inf;
    10     for(int _=fir[p],z;_;_=l[_])if((z=to[_])!=fa){
    11         dfs(z,p); int W=w[_];
    12         for(int i=0;i<=sz[p]+sz[z];++i)F[i]=G[i]=inf;
    13         for(int i=0;i<=sz[p];++i)for(int j=0;j<=sz[z];++j)
    14             G[max(0,i+j-1)]=min(G[max(0,i+j-1)],f[p][i]+f[z][j]),
    15             F[i+j]=min(F[i+j],f[p][i]+f[z][j]),
    16             F[i+j]=min(F[i+j],f[p][i]+g[z][j]+W),
    17             G[i+j]=min(G[i+j],f[p][i]+g[z][j]),
    18             F[i+j]=min(F[i+j],g[p][i]+f[z][j]),
    19             F[i+j+1]=min(F[i+j+1],g[p][i]+g[z][j]),
    20             G[i+j]=min(G[i+j],g[p][i]+g[z][j]+W);
    21         sz[p]+=sz[z];
    22         for(int i=0;i<=sz[p];++i)f[p][i]=F[i],g[p][i]=min(G[i],F[i]);
    23     }
    24 }
    25 int main(){//freopen("ex_newmzz3.in","r",stdin);
    26 while(cin>>n>>k>>c){
    27     for(int i=1,a,b,x;i<n;++i)scanf("%d%d%d",&a,&b,&x),con(a,b,x),tot+=x;
    28     dfs(0,0);
    29     int ans=inf;
    30     for(int i=0;i<=k;++i)ans=min(ans,min(g[0][i],f[0][i])+c*i);
    31     cout<<ans+tot<<endl;
    32     for(int i=0;i<n;++i)fir[i]=0; ec=tot=0;
    33 }}
    View Code

    这题的含义让人不难想到$wqs$二分。

    但是我们的要求是,第二维也就是已使用的路径数不能超过$k$。所以我们再开一个数组存储在此代价下最小的路径使用数。

    二分并据此查找合适的$c$

    如果对于某个特定的$c_0$最有决策下使用的路径书是$x$,对于$c_1$也是,那么在这两个代价下的决策也一定是完全一样的。

    所以说,我们可以先进行一次$dp$,然后看最有决策使用的路径数是否超过$k$

    如果没有超过那么就是说最优解就合法可以直接输出。否则,我们一定希望我们恰好选择$k$条路径。

    那么通过$wqs$去找恰好$k$条路径的方案,如果不存在恰好为$k$的,那么也当成$k$去计算就好了。

    (此时一定是存在多条路径,选择它们的收益相同,所以才会一选都选一不选都不选,然而恰好选$k$个是最优的,依次贡献答案是最佳的)

    总的时间复杂度是$O(n log w)$

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define S 100005
     4 #define ll long long
     5 ll f[S][2],tot;int g[S][2],n,fir[S],l[S<<1],to[S<<1],w[S<<1],ec,k,c;
     6 void link(int a,int b,int x){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;w[ec]=x;}
     7 void con(int a,int b,int x){link(a,b,x);link(b,a,x);}
     8 void upd(ll&F,int&G,int p,int z,int x,int y,int df,int dg){
     9     ll nf=df+f[p][x]+f[z][y],ng=dg+g[p][x]+g[z][y];
    10     if(nf<F||(nf==F&&ng<G))F=nf,G=ng;
    11 }
    12 void dfs(int p,int fa){
    13     f[p][1]=c; g[p][1]=1; f[p][0]=0; g[p][0]=0;
    14     for(int _=fir[p],z;_;_=l[_])if((z=to[_])!=fa){
    15         dfs(z,p); int W=w[_],g1,g0;ll f0=1e17,f1=1e17;
    16         upd(f0,g0,p,z,1,1,-c,-1);
    17         upd(f1,g1,p,z,1,1, 0, 0);
    18         upd(f1,g1,p,z,1,0, W, 0);
    19         upd(f0,g0,p,z,1,0, 0, 0);
    20         upd(f1,g1,p,z,0,1, 0, 0);
    21         upd(f1,g1,p,z,0,0, c, 1);
    22         upd(f0,g0,p,z,0,0, W, 0);
    23         f[p][1]=f1; f[p][0]=f0; g[p][1]=g1; g[p][0]=g0;
    24     }
    25 }
    26 int main(){//freopen("ex_newmzz3.in","r",stdin);
    27 while(cin>>n>>k>>c){
    28     for(int i=1,a,b,x;i<n;++i)scanf("%d%d%d",&a,&b,&x),con(a,b,x),tot+=x;
    29     dfs(0,0);
    30     int C=c,l=0,r=1000000000,b;ll ans=g[0][0]>k?1e18:f[0][0];
    31     while(c=l+r>>1,l<=r){
    32         dfs(0,0);
    33         if(g[0][0]<=k)r=c-1,b=c;else l=c+1;
    34     }
    35     c=b;dfs(0,0);
    36     ans=min(ans,f[0][0]+k*(C-c));
    37     cout<<ans+tot<<endl;
    38     for(int i=0;i<n;++i)fir[i]=0; ec=tot=0;
    39 }}
    View Code

    T2:计算几何

    看到题目名果断弃坑。

    T3:树形图求和

    大意:计算所有以$n$为根的内向树边权和。$n le 300,m le 10^5$

    原来$Matrix-Tree$是可以用在内向树之类的东西上的啊。

    对于内向树我们建基尔霍夫矩阵的时候,边只用单向边,度数只用出度,然后做$Matrix-Tree$的时候只要强制删掉的是根节点所在行列即可。

    首先求出整个图有多少生成内向树。

    然后只需要枚举每种边,把它去掉再求内向树个数,相减就能得到包含它的内向树个数。乘上权值就可以计算其贡献。

    复杂度$O(mn^3)$

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define mod 1000000007
     4 int A[55][55],n,m,w[55][55],ans;
     5 int mo(int a){return a>=mod?a-mod:a;}
     6 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;}
     7 int ret(int A[55][55]){
     8     int R=1,a[55][55];
     9     for(int i=1;i<n;++i)for(int j=1;j<n;++j)a[i][j]=A[i][j];
    10     for(int i=1;i<n;++i){
    11         R=1ll*R*a[i][i]%mod;
    12         for(int iv=qp(a[i][i],mod-2),j=i;j<n;++j)a[i][j]=1ll*a[i][j]*iv%mod;
    13         for(int j=i+1;j<n;++j)for(int k=n-1;k>=i;--k)a[j][k]=mo(a[j][k]-a[j][i]*1ll*a[i][k]%mod+mod);
    14     }return R;
    15 }
    16 int main(){
    17     cin>>n>>m;
    18     for(int i=1,x,y,z;i<=m;++i)scanf("%d%d%d",&x,&y,&z),w[x][y]=mo(w[x][y]+z),A[x][y]=mo(A[x][y]+mod-1),A[x][x]++,ans=mo(ans+z);
    19     ans=1ll*ans*ret(A)%mod;
    20     for(int x=1;x<=n;++x)for(int y=1;y<=n;++y)if(x!=y&&A[x][y])
    21         A[x][y]++,A[x][x]--,ans=(ans-1ll*ret(A)*w[x][y])%mod,A[x][y]--,A[x][x]++;
    22     cout<<mo(ans+mod)<<endl;
    23 }
    View Code

    复杂度瓶颈在于每次都要求行列式,于是我们引入(大量)线性代数知识:

    设$R$为$B$的余子式矩阵,则有拉普拉斯展开:

    $ret(B) = forall x sumlimits_{i=1}^{n} R_{x,i} B_{x,i}$

    $ret(B) = forall y sumlimits_{j=1}^{n} R_{j,y} B_{j,y}$

    其中,$R=(ret(B) B^{-1} )^T$

    (其实还有点中间过程,就是说:余子式矩阵是伴随矩阵的转置,伴随矩阵乘原矩阵是原矩阵行列式倍的单位矩阵)

    证明显然不会(也查不到)

    我们发现我们要求解行列式之前每次会删除一条边,矩阵的一行的两个元素发生改变,其余均不变。

    然后只改变第$x$行的元素那么这一行的余子式也不会改变(含义嘛,把这一行删掉了有什么影响)

    所以我们有了这一行的余子式,还有这一行矩阵的新值,我们就可以直接套用拉普拉斯展开来$O(n)$计算整行的行列式。

    只需要写一个矩阵求逆就好了。时间复杂度$O(n^3+mn)$

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define mod 1000000007
     4 int A[333][333],n,m,w[333][333],ans,R[333][333],RET;
     5 int mo(int a){return a>=mod?a-mod:a;}
     6 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;}
     7 int ret(int A[333][333]){
     8     int r=1,a[333][333];
     9     for(int i=1;i<n;++i)for(int j=1;j<n;++j)a[i][j]=A[i][j],R[i][j]=i==j;
    10     for(int i=1;i<n;++i){
    11         if(!a[i][i])for(int j=i+1;j<n;++j)if(a[j][i]){r=mod-r,swap(a[j],a[i]),swap(R[j],R[i]);break;}
    12         r=1ll*r*a[i][i]%mod;
    13         for(int iv=qp(a[i][i],mod-2),j=1;j<n;++j)a[i][j]=1ll*a[i][j]*iv%mod,R[i][j]=1ll*R[i][j]*iv%mod;
    14         for(int j=1;j<n;++j)if(j!=i)for(int z=a[j][i],k=n-1;k;--k)
    15             R[j][k]=mo(R[j][k]-z*1ll*R[i][k]%mod+mod),
    16             a[j][k]=mo(a[j][k]-z*1ll*a[i][k]%mod+mod);
    17     }return r;
    18 }
    19 int main(){
    20     cin>>n>>m;
    21     for(int i=1,x,y,z;i<=m;++i){
    22         scanf("%d%d%d",&x,&y,&z);
    23         if(x==n)continue;
    24         w[x][y]=mo(w[x][y]+z),A[x][y]=mo(A[x][y]+mod-1),A[x][x]++,ans=mo(ans+z);
    25     }
    26     RET=ret(A); ans=1ll*ans*RET%mod;
    27     for(int i=1;i<n;++i)for(int j=i+1;j<n;++j) swap(R[i][j],R[j][i]),R[i][j]=1ll*R[i][j]*RET%mod,R[j][i]=1ll*R[j][i]*RET%mod;
    28     for(int i=1;i<n;++i)R[i][i]=1ll*R[i][i]*RET%mod;
    29     for(int x=1;x<n;++x)for(int y=1;y<=n;++y)if(x!=y&&A[x][y]){
    30         A[x][y]++,A[x][x]--;
    31         for(int i=1;i<n;++i)ans=(ans-1ll*R[x][i]*A[x][i]%mod*w[x][y])%mod;
    32         A[x][y]--,A[x][x]++;
    33     }cout<<mo(ans+mod)<<endl;
    34 }
    View Code
  • 相关阅读:
    Svn如何使用,有什么作用?
    Unity脚本基础Day02
    unity设计模式-----责任链模式
    LitJson ---json的创建和解析
    Mesh编程——三角形,多边形,正方体,园形,圆环
    unity基础逻辑题
    unity——UI拖拽实现拼图
    unity:倒计时
    UGUI Toggle的监听事件绑定
    UnityGUI系统之InputField
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12717305.html
Copyright © 2011-2022 走看看