• [考试反思]0131省选模拟14:遗失


    100+0+40=140.rk3

    凑合?

    然而T2调试语句没删丢了20。(代码开头读入之前一个赫然的return 0)

    T2大概转化了题意,然后是杨氏矩阵的裸体了。然而我不会杨氏矩阵。

    尽力了。能打的都打了。还不错吧。。。

    中午就改完了T2,然后对着T3看了差不多一下午。。。

    感觉题解有锅啊。。。并没有人会解释。。。于是弃掉了

    T1:开车

    大意:图,第i条边权值为$2^i$。求每条边都经过一次的最短回路。$n le 100000, m le 500000$

    先强制每条边都走一次,于是每个点度数已知。只要让所有点度数都为偶数就形成了回路。

    由于边权的特殊性,前$i-1$条边加起来也没有第i条边大,所以一定是用最小生成树上的边来凑度数。

    跑最小生成树然后做个类似贪心的东西就没了。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define mod 1000000007
     4 #define S 888888
     5 int fir[S],l[S],to[S],v[S],d[S],n,m,f[S],V=1,ans,ec;
     6 void add(int&a,int b){a+=b;if(a>=mod)a-=mod;}
     7 int find(int p){return f[p]==p?p:f[p]=find(f[p]);}
     8 void link(int a,int b,int w){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=w;}
     9 int dfs(int p,int fa,int x=0){
    10     for(int i=fir[p];i;i=l[i])if(to[i]!=fa)x=dfs(to[i],p),add(ans,x*v[i]),d[p]+=x;
    11     return d[p]&1;
    12 }
    13 int main(){
    14     scanf("%d%d",&n,&m);
    15     for(int i=1;i<=n;++i)f[i]=i;
    16     for(int i=1,a,b;i<=m;++i){
    17         add(V,V);add(ans,V);
    18         scanf("%d%d",&a,&b);d[a]++;d[b]++;
    19         if(find(a)!=find(b))f[f[a]]=f[b],link(a,b,V),link(b,a,V);
    20     }dfs(1,0);printf("%d
    ",ans);
    21 }
    View Code

    T2:上分

    大意:若要解锁(i,j)必须解锁(i,j-1),(i-1,j)。(x,y)在x<y时无需解锁。给定两点(a,b),(c,d)。求解锁这两个点在保证总解锁点数最小的情况下解锁顺序方案数。

    $b le a,d le c, a,cle 1000000$保证最小总解锁点数小于等于1000000。

    斜着的坐标系看着难受,转过来。把(i,j)变成(i-j+1,j)。这样限制就变成了必须先解锁左边和上边的元素。

    然后就是杨氏矩阵裸题了。

    杨氏矩阵是指一类:如果这个点有元素,那么右方和下方要么有元素,要么比这个点大。这样的矩阵就是杨氏矩阵。

    引入钩子引理:$P=frac{n!}{prod h_{i,j}}$。P是指在确定杨氏矩阵的形状的情况下,在每个有元素的位置上填$1$到$n$使之满足杨氏矩阵性质的方案数。

    $h_{i,j}$表示$(i,j)$这个点的钩子长:右方和下方(不含)的元素个数和+1。

    这道题杨氏矩阵的形状就是一个矩形,或两个相交的左上角重叠的矩形。

    预处理阶乘的前缀积,用那种二位前缀和一样的思路差分一下即可。

     1 #include<cstdio>
     2 #include<iostream>
     3 using namespace std;
     4 #define mod 1000000007
     5 int fac[1234567],a,b,c,d,ffac[1234567],iinv[1234567];
     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 cal(int a,int b){return a*b?1ll*ffac[a+b-1]*iinv[a-1]%mod*iinv[b-1]%mod:1;}
     8 int ical(int a,int b){return a*b?1ll*iinv[a+b-1]*ffac[a-1]%mod*ffac[b-1]%mod:1;}
     9 int cal(int a,int b,int c,int d){return 1ll*ical(a,b-d)*ical(d,c-a)%mod*ical(b,c)%mod*ical(b-d,c-a)%mod*cal(c-a,b)%mod*cal(c,b-d)%mod;}
    10 int main(){fac[0]=ffac[0]=iinv[0]=1;//freopen("1.in","r",stdin);
    11     for(int i=1;i<=1000000;++i)fac[i]=fac[i-1]*1ll*i%mod,ffac[i]=ffac[i-1]*1ll*fac[i]%mod,iinv[i]=qp(ffac[i],mod-2);
    12     int t,A,B,C,D;scanf("%d",&t);
    13     while(t-->0){
    14         scanf("%d%d%d%d",&A,&B,&C,&D);
    15         A-=B-1;C-=D-1;
    16         if(A>C)swap(A,C),swap(B,D);
    17         if(B<=D)printf("%lld
    ",1ll*fac[C*D]*ical(C,D)%mod);
    18         else printf("%lld
    ",1ll*fac[A*B+C*D-A*D]*cal(A,B,C,D)%mod);
    19     }
    20 }
    View Code

    T3:隔膜

    大意:博弈,轮流操作n个有价值硬币,最开始第奇数个正面朝上其他反面。第$i$次操作可以在第$i,i+1$两个硬币中选一个翻面或者啥也不干。奇数次操作由先手方执行。

    先手方得分是最后所有正面朝上的硬币价值和。求$n-1$次操作后最优决策下先手方得分。$m$次修改,每次修改会降低一个硬币的权值,保证时刻为正。每次修改后求解。

    $n,m le 200000$

    暴力是一个比较经典的博弈论的倒着dp的思路。设dp[i][0/1]表示第i次操作前硬币i正反面朝上时,两人后续采取最优决策时先手方的得分。         

    暴力就可以写了。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int n,m,a[222222];long long dp[2][222222];
     4 int main(){
     5     scanf("%d",&n);
     6     for(int i=1;i<=n;++i)scanf("%d",&a[i]);
     7     dp[1][n]=a[n];
     8     for(int i=n-1;i;--i)
     9         if(i&1)dp[1][i]=a[i]+dp[1][i+1],dp[0][i]=max(a[i]+dp[0][i+1],dp[1][i+1]);
    10         else dp[0][i]=dp[0][i+1],dp[1][i]=min(dp[1][i+1],dp[0][i+1]+a[i]);
    11     printf("%lld
    ",dp[1][1]);
    12     scanf("%d",&m);
    13     while(m-->0){int p,d;
    14         scanf("%d%d",&p,&d);a[p]-=d;
    15         dp[1][n]=a[n];
    16         for(int i=min(n-1,p);i;--i)
    17             if(i&1)dp[1][i]=a[i]+dp[1][i+1],dp[0][i]=max(a[i]+dp[0][i+1],dp[1][i+1]);
    18             else dp[0][i]=dp[0][i+1],dp[1][i]=min(dp[1][i+1],dp[0][i+1]+a[i]);
    19         printf("%lld
    ",dp[1][1]);
    20     }
    21 }
    View Code

    正解的话说的很有道理,但是不会证明。

    说是在的代码不会很难写,应该说很简单。但是不会证写着就很没意思,所以先鸽了。

  • 相关阅读:
    JAVA多线程(七) ReentrantLock原理分析
    JAVA多线程(六) synchronize原理分析
    JAVA多线程(五) volatile原理分析
    pandas
    从美国总经理,到三一重卡的董事长,梁林河的重卡梦
    【转载】低水平领导的十大表现
    kill及其衍生程序
    python -m venv 的使用
    Python多版本启动器
    Pyinstaller最流行的打包程序
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12246901.html
Copyright © 2020-2023  润新知