zoukankan      html  css  js  c++  java
  • 2019牛客暑期多校训练营(第二场)

    Solutions


    A:Eddy Walker

    题意:

    给出$n$个点,编号为$0sim n-1$,问走完所有点恰巧到达$m$点的概率。

    思路:

    如果,只要$m$不为$0$,到其他点概率都是一样的,为$frac1{n-1}$,然后判$m=0$的情况。

     1 //#define DEBUG
     2 #include<bits/stdc++.h>
     3 using namespace std;
     4 const int N=100010;
     5 const int inf=0X3f3f3f3f;
     6 const long long INF = 0x3f3f3f3f3f3f3f3f;
     7 const double eps = 1e-6;
     8 const double pi = acos(-1.0);
     9 const int mod = 1000000007;
    10 typedef long long ll;
    11 
    12 ll q_pow(ll a,ll b) {
    13     ll ans=1;
    14     ll tmp=a%mod;
    15     while(b) {
    16         if(b&1) ans=ans*tmp%mod;
    17         tmp=tmp*tmp%mod;
    18         b=b>>1;
    19     }
    20     return ans;
    21 }
    22 
    23 int main() {
    24     #ifdef DEBUG
    25     freopen("in.txt","r",stdin);
    26     #endif
    27     int _;
    28     ll ans=1;
    29     for(scanf("%d",&_);_;_--) {
    30         ll n,m;
    31         scanf("%lld%lld",&n,&m);
    32         if(n==1&&m==0) ans*=1;
    33         else if(m==0&&n>1) ans=0;
    34         else ans=ans*q_pow(n-1,mod-2)%mod;
    35         printf("%lld
    ",ans);
    36     }
    37 }

    B:Eddy Walker 2

    题意:

    某人开始在$0$点,给出目标点$k$,和每步能走的最大距离$k$,且走距离$1sim k$概率都为$frac1k$,求最后到达$n$点的概率,$n=-1$表示无穷远。

    思路:

    $dp[i]$表示到达$i$点的概率,那么

    $dp[i]=frac1ksum_{j=i-k}^{i-1}dp[j]$,线性递推式,丢进BM里搞一搞

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <cmath>
      4 #include <algorithm>
      5 #include <vector>
      6 #include <string>
      7 #include <map>
      8 #include <set>
      9 #include <cassert>
     10 #include<bits/stdc++.h>
     11 using namespace std;
     12 #define rep(i,a,n) for (int i=a;i<n;i++)
     13 #define per(i,a,n) for (int i=n-1;i>=a;i--)
     14 #define pb push_back
     15 #define mp make_pair
     16 #define all(x) (x).begin(),(x).end()
     17 #define fi first
     18 #define se second
     19 #define SZ(x) ((int)(x).size())
     20 typedef vector<int> VI;
     21 typedef long long ll;
     22 typedef pair<int,int> PII;
     23 const ll mod=1000000007;
     24 ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
     25 // head
     26 
     27 namespace linear_seq {
     28     const int N=10010;
     29     ll res[N],base[N],_c[N],_md[N];
     30 
     31     vector<int> Md;
     32     void mul(ll *a,ll *b,int k) {
     33         rep(i,0,k+k) _c[i]=0;
     34         rep(i,0,k) if (a[i]) rep(j,0,k) _c[i+j]=(_c[i+j]+a[i]*b[j])%mod;
     35         for (int i=k+k-1;i>=k;i--) if (_c[i])
     36             rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod;
     37         rep(i,0,k) a[i]=_c[i];
     38     }
     39     int solve(ll n,VI a,VI b) { // a 系数 b 初值 b[n+1]=a[0]*b[n]+...
     40         ll ans=0,pnt=0;
     41         int k=SZ(a);
     42         assert(SZ(a)==SZ(b));
     43         rep(i,0,k) _md[k-1-i]=-a[i];_md[k]=1;
     44         Md.clear();
     45         rep(i,0,k) if (_md[i]!=0) Md.push_back(i);
     46         rep(i,0,k) res[i]=base[i]=0;
     47         res[0]=1;
     48         while ((1ll<<pnt)<=n) pnt++;
     49         for (int p=pnt;p>=0;p--) {
     50             mul(res,res,k);
     51             if ((n>>p)&1) {
     52                 for (int i=k-1;i>=0;i--) res[i+1]=res[i];res[0]=0;
     53                 rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod;
     54             }
     55         }
     56         rep(i,0,k) ans=(ans+res[i]*b[i])%mod;
     57         if (ans<0) ans+=mod;
     58         return ans;
     59     }
     60     VI BM(VI s) {
     61         VI C(1,1),B(1,1);
     62         int L=0,m=1,b=1;
     63         rep(n,0,SZ(s)) {
     64             ll d=0;
     65             rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod;
     66             if (d==0) ++m;
     67             else if (2*L<=n) {
     68                 VI T=C;
     69                 ll c=mod-d*powmod(b,mod-2)%mod;
     70                 while (SZ(C)<SZ(B)+m) C.pb(0);
     71                 rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
     72                 L=n+1-L; B=T; b=d; m=1;
     73             } else {
     74                 ll c=mod-d*powmod(b,mod-2)%mod;
     75                 while (SZ(C)<SZ(B)+m) C.pb(0);
     76                 rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
     77                 ++m;
     78             }
     79         }
     80         return C;
     81     }
     82     int gao(VI a,ll n) {
     83         VI c=BM(a);
     84         c.erase(c.begin());
     85         rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod;
     86         return solve(n,c,VI(a.begin(),a.begin()+SZ(c)));
     87     }
     88 };
     89 
     90 ll dp[3000];
     91 int main() {
     92     int _;
     93     for(scanf("%d",&_);_;_--) {
     94         int k;
     95         ll n;
     96         scanf("%d%lld",&k,&n);
     97         if(n==-1) {
     98             printf("%lld
    ",2*powmod(k+1,mod-2)%mod);
     99         } else {
    100             VI t;
    101             dp[0]=1;
    102             t.push_back(dp[0]);
    103             for(int i=1;i<=2*k;i++) {
    104                 dp[i]=0;
    105                 for(int j=max(0,i-k);j<i;j++) {
    106                     dp[i]=(dp[i]+dp[j])%mod;
    107                 }
    108                 dp[i]=dp[i]*powmod(k,mod-2)%mod;
    109                 t.push_back(dp[i]);
    110             }
    111             printf("%lld
    ",linear_seq::gao(t,n));
    112         }
    113     }
    114 }

    D:Kth Minimum Clique

    题意:

    给出$n$个点以及点权,求第$k$小团。空团是第一小。

    思路:

    可以拿着空的,往里面放。每次取出最小的,往里放点,放的时候判断一下是否和团里面的都有边,判断可以用$bitset$优化

    放的时候,编号递增的放,防止放入重复点。

     1 //#define DEBUG
     2 #include<bits/stdc++.h>
     3 using namespace std;
     4 const int N=100010;
     5 const int inf=0X3f3f3f3f;
     6 const long long INF = 0x3f3f3f3f3f3f3f3f;
     7 const double eps = 1e-6;
     8 const double pi = acos(-1.0);
     9 const int mod = 1000000007;
    10 typedef long long ll;
    11 
    12 struct Node{
    13     ll w;
    14     bitset<110> S;
    15     bool operator < (const Node& b) const {
    16         return w>b.w;
    17     }
    18 };
    19 
    20 char buf[110];
    21 bitset<110> bs[110];
    22 ll a[110];
    23 
    24 int main() {
    25     #ifdef DEBUG
    26     freopen("in.txt","r",stdin);
    27     #endif
    28     int n,k;
    29     scanf("%d%d",&n,&k);
    30     for(int i=0;i<n;i++) scanf("%lld",&a[i]);
    31     for(int i=0;i<n;i++) {
    32         scanf("%s",buf);
    33         for(int j=0;buf[j];j++) {
    34             if(buf[j]=='1') bs[i][j]=1;
    35         }
    36     }
    37     priority_queue<Node> pq;
    38     bitset<110> t;
    39     pq.push((Node){0,t});
    40     while(!pq.empty()) {
    41         Node f=pq.top();pq.pop();
    42         bitset<110> r=f.S;
    43         k--;
    44         if(k==0) {
    45             printf("%lld
    ",f.w);
    46             return 0;
    47         }
    48         int pos=0;
    49         for(int i=0;i<n;i++) {
    50             if(r[i]) {
    51                 pos=i+1;
    52             }
    53         }
    54         for(int i=pos;i<n;i++) {
    55             if(!r[i]&&(bs[i]&r)==r) {
    56                 r[i]=1;
    57                 pq.push((Node){f.w+a[i],r});
    58                 r[i]=0;
    59             }
    60         }
    61     }
    62     puts("-1");
    63 }

    E:MAZE

    题意:

    有一个$n imes m$的$01$矩阵,$1$表示不可行,$0$表示可行

    每次可以从$left(i,j ight)$走到$left(i,j-1 ight),left(i,j+1 ight),left(i+1,j ight)$,且不能回到走过的格子

    有$q$个以下两种欧冠操作:

    1. 将某个格子的状态反转
    2. 询问从$left(1,x ight)走到left(n,y ight)$的方案数
    • $1leq n$,$qleq 5e4$,$1leq mleq10$

    思路:

    参考123Bolgs

    先不考虑修改,设$a[i][j]$表示第$i$行第$j$个元素

    $dp[i][j]$表示能从第$i-1$行经过$left(i-1,j ight)$走到$left(i,j ight)$的方案数,状态转移方程如下:

    $dp[i][j]=sum_{k=1}^{j-1}{dp[i-1][k]}_{for a[i-1][k]=0} +sum_{k=j}^m {dp[i-1][k]}_{for a[i-1][k]=0}$

    比如,$n=2,m=6$时,

    $dp[2][2]=dp[1][1]+dp[1][2]+dp[1][3]$

    $dp[2][6]=dp[1][5]+dp[1][6]$

    我们可以用“列可达”矩阵表示转移关系,然后乘起来就是最后的结果。

    “列可达”比如第一行的列可达,第一列为$0$,所以$left(0,0 ight)$位置为$1$,它能到达$2,3$列所以$left(2,1 ight),left(3,1 ight)$为$1$,遇到$1$结束。(见下图)

    依次类推。

    也就是这样:

    发现两个矩阵相乘就可以查询答案了。可以用线段树维护矩阵相乘,每个节点维护一个矩阵(一行)。

    修改的话,就修改维护那一行的节点。

     1 //#define DEBUG
     2 #include<bits/stdc++.h>
     3 using namespace std;
     4 const int N=50010;
     5 const int inf=0X3f3f3f3f;
     6 const long long INF = 0x3f3f3f3f3f3f3f3f;
     7 const double eps = 1e-6;
     8 const double pi = acos(-1.0);
     9 const int mod = 1000000007;
    10 typedef long long ll;
    11 
    12 int n,m,a[N][15];
    13 
    14 struct Mat{
    15     ll a[15][15];
    16     void init() {memset(a,0,sizeof(a));}
    17     Mat operator * (const Mat &b)const {
    18         Mat ans;
    19         ans.init();
    20         for(int i=1;i<=m;i++) {
    21             for(int j=1;j<=m;j++) {
    22                 for(int k=1;k<=m;k++) {
    23                     ans.a[i][j]=(ans.a[i][j]+a[i][k]*b.a[k][j]%mod)%mod;
    24                 }
    25             }
    26         }
    27         return ans;
    28     }
    29 }mat[N<<2];
    30 
    31 void mul(int rt,int x) {
    32     mat[rt].init();
    33     for(int i=1;i<=m;i++) {
    34         if(a[x][i]) continue;
    35         mat[rt].a[i][i]=1;
    36         for(int k=i+1;k<=m&&(!a[x][k]);k++)
    37             mat[rt].a[k][i]=1;
    38         for(int k=i-1;k>=1&&(!a[x][k]);k--)
    39             mat[rt].a[k][i]=1;
    40     }
    41 }
    42 
    43 void build(int l,int r,int rt) {
    44     if(l==r) {
    45         mul(rt,l);
    46         return ;
    47     }
    48     int mid=(l+r)>>1;
    49     build(l,mid,rt<<1);
    50     build(mid+1,r,rt<<1|1);
    51     mat[rt]=mat[rt<<1]*mat[rt<<1|1];
    52 }
    53 
    54 void updata(int v,int l,int r,int rt) {
    55     if(l==r) {
    56         mul(rt,l);
    57         return ;
    58     }
    59     int mid=(l+r)>>1;
    60     if(v<=mid) updata(v,l,mid,rt<<1);
    61     else updata(v,mid+1,r,rt<<1|1);
    62     mat[rt]=mat[rt<<1]*mat[rt<<1|1];
    63 }
    64 
    65 int main() {
    66     #ifdef DEBUG
    67     freopen("in.txt","r",stdin);
    68     #endif
    69     int q;
    70     scanf("%d%d%d",&n,&m,&q);
    71     for(int i=1;i<=n;i++) {
    72         for(int j=1;j<=m;j++)
    73             scanf("%1d",&a[i][j]);
    74     }
    75     build(1,n,1);
    76     while(q--) {
    77         int op,x,y;
    78         scanf("%d%d%d",&op,&x,&y);
    79         if(op==1) {
    80             a[x][y]^=1;
    81             updata(x,1,n,1);
    82         } else printf("%lld
    ",mat[1].a[x][y]);
    83     }
    84     return 0;
    85 }

    F:Partition problem

    题意:

    $2n$个人,每个人都与其他人有对抗值,分成两个集合且集合大小相等,求分配后的最大对抗值

    思路:

    啥都不说,就是不会$DFS$,感觉还做过。给出链接

     1 //#define DEBUG
     2 #include<bits/stdc++.h>
     3 using namespace std;
     4 const int N=100010;
     5 const int inf=0X3f3f3f3f;
     6 const long long INF = 0x3f3f3f3f3f3f3f3f;
     7 const double eps = 1e-6;
     8 const double pi = acos(-1.0);
     9 const int mod = 1000000007;
    10 typedef long long ll;
    11 
    12 ll a[30][30],ans;
    13 int sa[30],sb[30];
    14 int n;
    15 
    16 void dfs(int id,int num1,int num2,ll sum) {
    17     if(num1==n&&num2==n) {
    18         for(int i=0;i<num1;i++) printf("%d ",sa[num1]);
    19         puts("");
    20         for(int i=0;i<num2;i++) printf("%d ",sb[num2]);
    21         puts("");
    22         ans=max(ans,sum);
    23         return;
    24     }
    25     if(num1<n) {
    26         ll tmp=0;
    27         for(int i=0;i<num2;i++) tmp+=a[id][sb[i]];
    28         sa[num1]=id;
    29         dfs(id+1,num1+1,num2,sum+tmp);
    30     }
    31     if(num2<n) {
    32         ll tmp=0;
    33         for(int i=0;i<num1;i++) tmp+=a[id][sa[i]];
    34         sb[num2]=id;
    35         dfs(id+1,num1,num2+1,sum+tmp);
    36     }
    37 }
    38 int main() {
    39     #ifdef DEBUG
    40     freopen("in.txt","r",stdin);
    41     #endif
    42     scanf("%d",&n);
    43     for(int i=1;i<=2*n;i++) {
    44         for(int j=1;j<=2*n;j++)
    45             scanf("%lld",&a[i][j]);
    46     }
    47     dfs(1,0,0,0);
    48     printf("%lld
    ",ans);
    49 }

    H:Second Large Rectangle

    题意:

    求次大全$1$矩阵的面积。

    思路:

    首先最大全$1$矩阵可以通过单调栈求出,用同样的方法,把二维的压下来,每一行存当前高度,然后每一行都用单调栈维护,求得的矩阵去更新答案,注意!!!$xast(y-1),yast(x-1)$也要更新答案。。。当时没理解,加入就AC了。。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int maxn=100010;
     5 const int inf=0x3f3f3f3f;
     6 
     7 int a[1010][1010];
     8 int n,m;
     9 stack<pair<int,int> > s;
    10 vector<int> ansans;
    11 int main() {
    12     while(~scanf("%d%d",&n,&m)) {
    13         for(int i=1;i<=n;i++) {
    14             for(int j=1;j<=m;j++) {
    15                 scanf("%1d",&a[i][j]);
    16                 if(a[i][j]) a[i][j]+=a[i-1][j];
    17             }
    18             a[i][m+1]=0;
    19         }
    20         pair<int,int> v;
    21         for(int i=1;i<=n;i++) {
    22             for(int j=1;j<=m+1;j++) {
    23                 if(s.empty()||s.top().second<=a[i][j]) {
    24                     s.push({1,a[i][j]});
    25                 }
    26                 else {
    27                     int sum=0;
    28                     while(!s.empty()&&s.top().second>a[i][j]) {
    29                         v=s.top();
    30                         sum+=v.first;
    31                         int area1=sum*v.second;
    32                         int area2=(sum-1)*v.second;
    33                         int area3=sum*(v.second-1);
    34                         ansans.push_back(area1);
    35                         ansans.push_back(area2);
    36                         ansans.push_back(area3);
    37                         s.pop();
    38                     }
    39                     s.push({sum+1,a[i][j]});
    40                 }
    41             }
    42             while(!s.empty()) s.pop();
    43         }
    44         sort(ansans.begin(),ansans.end());
    45         int len=ansans.size();
    46         if(len<2) printf("0
    ");
    47         else {
    48             printf("%d
    ",ansans[len-2]);
    49         }
    50         while(!s.empty()) s.pop();
    51         ansans.clear();
    52     }
    53 }
  • 相关阅读:
    三、oneinstack
    二、两条Linux删除数据跑路命令
    一、定时任务
    三、Docker网络
    C#数据结构
    四、字段大全
    【2019-06-14】同事的低迷
    【2019-06-13】同事的进步
    【2019-06-12】看书的好地方
    【2019-06-11】笔耕不辍
  • 原文地址:https://www.cnblogs.com/ACMerszl/p/11230619.html
Copyright © 2011-2022 走看看