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
  • 相关阅读:
    安全编码1
    VPP tips
    VPP概述汇总
    C语言安全编码摘录
    TCP-proxy
    Scipy Lecture Notes学习笔记(一)Getting started with Python for science 1.4. Matplotlib: plotting
    Scipy Lecture Notes学习笔记(一)Getting started with Python for science 1.3. NumPy: creating and manipulating numerical data
    Scipy Lecture Notes学习笔记(一)Getting started with Python for science 1.2. The Python language
    Scipy Lecture Notes学习笔记(一)Getting started with Python for science 1.1. Python scientific computing ecosystem
    25马5跑道,求最快的五匹马的需要比赛的次数
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12717305.html
Copyright © 2011-2022 走看看