2019牛客多校第二場

A Eddy Walker

題意

一個0到n-1的環,初始在0,每次隨機前進或後退一格,當全部點都訪問過就結束,問結束時站在k的機率是多少。(注意輸出的是前綴積)node

分析

一開始站在0,最後顯然不可能在0,剩下n-1格,隨機數打表發現機率相同,都爲\(\frac{1}{n-1}\)c++

代碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int cnt[1050];
bool vis[1050];
void sui(int n){
    memset(cnt,0,sizeof(cnt));
    int num=100000;
    while(num--){
        memset(vis,false,sizeof(vis));
        vis[0]=true;
        int now=0;
        while(true){
            bool flag=true;
            for(int i=0;i<n;i++){
                if(!vis[i]){
                    flag=false;
                }
            }
            if(flag){
                break;
            }
            int rn=rand()%10;
            if(rn<5){
                now=(now-1+n)%n;
            }else{
                now=(now+1)%n;
            }
            vis[now]=true;
        }
        cnt[now]++;
    }
    for(int i=0;i<n;i++){
        printf("%d%c",cnt[i],i==n-1?'\n':' ');
    }
}
ll Pow(ll a,ll n){
    ll ans=1ll;
    while(n){
        if(n%2){
            ans=ans*a%mod;
        }
        a=a*a%mod;
        n/=2;;
    }
    return ans;
}
int t,n,m;
int main(){
    scanf("%d",&t);
    ll ans=1ll;
    while(t--){
        scanf("%d%d",&n,&m);
        ll tmp;
        if(m==0){
            if(n==1){
                tmp=1ll;
            }else{
                tmp=0;
            }
        }else{
            tmp=Pow(1ll*(n-1),mod-2);
        }
        ans=(ans*tmp)%mod;
        printf("%lld\n",ans);
    }
}

B Eddy Walker 2

題意

無限長的路,每次能夠向前走1-k步,機率相同,問走到n的機率。算法

分析

相似於經典dp走樓梯,設dp[i]爲走到i的機率,那麼\(dp[i]=\frac{1}{k}\sum_{j=i-k}^{i-1}dp[j]\),這是一種線性遞推的關係,因此求出前幾項,而後用BM算法求出第n項。ui

當n爲無窮大的狀況,先計算走一步的指望,\(\sum_{i=1}^k\frac{i}{k}=\frac{k+1}{2}\),那麼反過來,走到某一格的機率就是\(\frac{1}{\frac{k+1}{2}}=\frac{2}{k+1}\)spa

代碼

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef long long ll;
typedef vector<ll> VI;
typedef pair<int,int> PII;
const ll mod=1000000007;
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;}
// head
ll n;
namespace linear_seq {
    const int N=10010;
    ll res[N],base[N],_c[N],_md[N];

    vector<int> Md;
    void mul(ll *a,ll *b,int k) {
        rep(i,0,k+k) _c[i]=0;
        rep(i,0,k) if (a[i]) rep(j,0,k) _c[i+j]=(_c[i+j]+a[i]*b[j])%mod;
        for (int i=k+k-1;i>=k;i--) if (_c[i])
            rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod;
        rep(i,0,k) a[i]=_c[i];
    }
    int solve(ll n,VI a,VI b) { // a 係數 b 初值 b[n+1]=a[0]*b[n]+...
        ll ans=0,pnt=0;
        int k=SZ(a);
        assert(SZ(a)==SZ(b));
        rep(i,0,k) _md[k-1-i]=-a[i];_md[k]=1;
        Md.clear();
        rep(i,0,k) if (_md[i]!=0) Md.push_back(i);
        rep(i,0,k) res[i]=base[i]=0;
        res[0]=1;
        while ((1ll<<pnt)<=n) pnt++;
        for (int p=pnt;p>=0;p--) {
            mul(res,res,k);
            if ((n>>p)&1) {
                for (int i=k-1;i>=0;i--) res[i+1]=res[i];res[0]=0;
                rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod;
            }
        }
        rep(i,0,k) ans=(ans+res[i]*b[i])%mod;
        if (ans<0) ans+=mod;
        return ans;
    }
    VI BM(VI s) {
        VI C(1,1),B(1,1);
        int L=0,m=1,b=1;
        rep(n,0,SZ(s)) {
            ll d=0;
            rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod;
            if (d==0) ++m;
            else if (2*L<=n) {
                VI T=C;
                ll c=mod-d*powmod(b,mod-2)%mod;
                while (SZ(C)<SZ(B)+m) C.pb(0);
                rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                L=n+1-L; B=T; b=d; m=1;
            } else {
                ll c=mod-d*powmod(b,mod-2)%mod;
                while (SZ(C)<SZ(B)+m) C.pb(0);
                rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                ++m;
            }
        }
        return C;
    }
    int gao(VI a,ll n) {
        VI c=BM(a);
        c.erase(c.begin());
        rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod;
        return solve(n,c,VI(a.begin(),a.begin()+SZ(c)));
    }
};
vector<ll> v;
int t,k;
ll dp[2050];
int main(void){
    // freopen("in.txt","r",stdin);
    scanf("%d",&t);
    while(t--){
        scanf("%d%lld",&k,&n);
        ll invk=powmod(k,mod-2);
        memset(dp,0,sizeof(dp));
        dp[0]=1;
        v.clear();
        v.push_back(1);
        for(int i=1;i<=2*k;i++){
            dp[i]=0;
            for(int j=max(0,i-k);j<i;j++){
                dp[i]=(dp[i]+dp[j])%mod;
            }
            dp[i]=(dp[i]*invk)%mod;
            // printf("%lld\n",dp[i]);
            v.push_back(dp[i]);
        }
        ll ans;
        if(n==-1){
            ll invk_1=powmod(k+1,mod-2);
            ans=2ll*invk_1%mod;
        }else{
            ans=linear_seq::gao(v,n);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

D Kth Minimum Clique

題意

給一個圖,求第k小團(徹底子圖)。code

分析

用bitset狀壓表示當前團,而後每次取出最小的,嘗試加入一個點,知足該點和團內全部點都有邊相連,用位運算實現,第k個取出的即爲第k小團。it

爲了不重複,標記每一個團是由那個點擴大而來的。io

代碼

#include <bits/stdc++.h>
using namespace std;
const int N=105;
typedef long long ll;
typedef bitset<N> bs;
int n,k;
ll a[N];
char s[N];
struct node{
    int id;
    ll val;
    //當前團的狀態
    bs sta;
    bool operator <(const node& rhs)const{
        return val>rhs.val;
    }
};
bs g[N];
bs t;
priority_queue<node> pq;
int main(void){
    // freopen("in.txt","r",stdin);
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++){
        scanf("%lld",&a[i]);
    }
    for(int i=0;i<n;i++){
        scanf("%s",s);
        for(int j=0;j<n;j++){
            g[i][j]=s[j]-'0';
        }
    }
    if(k==1){
        printf("0\n");
        return 0;
    }
    k--;
    for(int i=0;i<n;i++){
        t.reset();
        t[i]=1;
        pq.push(node{i,a[i],t});
    }
    while(!pq.empty()){
        node tmp=pq.top();
        pq.pop();
        if(k==1){
            printf("%lld\n",tmp.val);
            return 0;
        }
        k--;
        for(int i=tmp.id+1;i<n;i++){
            //若是一個點所連的全部點(g[i]恰好是當前團的全部點sta,那麼加入i後仍然是團)
            if((g[i]&tmp.sta)==tmp.sta){
                bs now=tmp.sta;
                now[i]=1;
                pq.push(node{i,tmp.val+a[i],now});
            }
        }
    }
    printf("-1\n");
    return 0;
}

E MAZE

題意

給一個01矩陣,能夠往左,右,下走,且只有0能夠走,兩種操做,修改某個點狀態,詢問從(1,a)到(n,b)的方案數。class

分析

定義狀態,\(dp[i][j]\)表示從\(i-1\)行通過\(g[i-1][j]\)走到\(g[i][j]\)的方案數,所以\(dp[i][j]=\sum_{j=i}^{r}dp[i-1][j]+\sum_{j=i-1}^{l}dp[i-1][j]\),其中\(l\)\(r\)表示\(g[i-1][j]\),左右連續0延伸的位置。cli

因此答案顯然就是\(dp[n+1][b]\),由於\(dp[n][b]\)只考慮了從\(g[n-1][b]\)往下走的方案數。

顯然從第一層到第二層狀態的轉移是一個線性遞推的式子,中間能夠構造一個矩陣,那麼從第一層到第n+1層的狀態顯然就是n個矩陣的乘積了,所求結果就是\((\sum_{i=1}^nM_i)[a][b]\)

修改的操做直接修改矩陣,而後線段樹單點修改便可。

代碼

#include <bits/stdc++.h>
using namespace std;
#define ls i<<1
#define rs i<<1|1
#define mid (l+r)/2
typedef long long ll;
const int N=50050;
const int M=12;
const ll mod=1e9+7;
int n,m,q,o,a,b;
int g[N][M];
char s[M];
struct Mat{
    ll m[M][M];
    void init(){
        memset(m,0,sizeof(m));
    }
};
Mat mMul(Mat a,Mat b){
    Mat ans;
    ans.init();
    for(int i=0;i<M;i++){
        for(int j=0;j<M;j++){
            ans.m[i][j]=0;
            for(int k=0;k<M;k++){
                ans.m[i][j]+=(a.m[i][k]*b.m[k][j]%mod);
                ans.m[i][j]%=mod;
            }
        }
    }
    return ans;
}
//線段樹維護區間矩陣連乘
Mat pro[N*4];
void pushup(int i){
    pro[i]=mMul(pro[ls],pro[rs]);
}
void build(int i,int l,int r){
    if(l==r){
        //先構造出每一層的轉移矩陣
        // pro[i].init();
        for(int j=1;j<=m;j++){
            //g上一行連續0的部分在轉移矩陣中爲1
            for(int k=j;k<=m;k++){
                if(g[l][k]==0){
                    pro[i].m[j][k]=1;
                }else{
                    break;
                }
            }
            for(int k=j;k>=1;k--){
                if(g[l][k]==0){
                    pro[i].m[j][k]=1;
                }else{
                    break;
                }
            }
        }
        return;
    }
    build(ls,l,mid);
    build(rs,mid+1,r);
    pushup(i);
}
void update(int i,int l,int r,int a,int b){
    if(l==r && a==l){
        //單點更新,重置轉移矩陣
        pro[i].init();
        for(int j=1;j<=m;j++){
            //g上一行連續0的部分在轉移矩陣中爲1
            for(int k=j;k<=m;k++){
                if(g[a][k]==0){
                    pro[i].m[j][k]=1;
                }else{
                    break;
                }
            }
            for(int k=j;k>=1;k--){
                if(g[a][k]==0){
                    pro[i].m[j][k]=1;
                }else{
                    break;
                }
            }
        }
        return;
    }
    if(a<=mid){
        update(ls,l,mid,a,b);
    }else{
        update(rs,mid+1,r,a,b);
    }
    pushup(i);
}
int main(void){
    // freopen("in.txt","r",stdin);
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        for(int j=1;j<=m;j++){
            g[i][j]=(s[j]-'0');
        }
    }
    build(1,1,n);
    //對於每個查詢 答案就是(\prod_{i-1}^n Mi)[a][b]
    while(q--){
        scanf("%d%d%d",&o,&a,&b);
        if(o==1){
            g[a][b]^=1;
            update(1,1,n,a,b);
        }else if(o==2){
            printf("%lld\n",pro[1].m[a][b]%mod);
        }
    }
    return 0;
}

F Partition problem

題意

將2n我的分爲兩隊,給定一個n*n的權值矩陣,定義總權值爲兩隊各n我的分別的權值和,求最大總權值。

分析

直接暴力搜索,每加入一個就算貢獻,不要等到所有分完再計算。

代碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll g[30][30];
int n;
ll ans;
// vector<int> a,b;
int a[30],b[30];
int ac,bc;
void dfs(int idx,ll now){
    if(ac==n && bc==n){
        ans=max(ans,now);
        return;
    }
    if(ac<n){
        ll t=0;
        for(int i=0;i<bc;i++){
            t+=g[idx][b[i]];
        }
        now+=t;
        a[ac++]=idx;
        dfs(idx+1,now);
        a[--ac]=0;
        now-=t;
    }
    if(bc<n){
        ll t=0;
        for(int i=0;i<ac;i++){
            t+=g[idx][a[i]];
        }
        now+=t;
        b[bc++]=idx;
        dfs(idx+1,now);
        b[--bc]=0;
        now-=t;
    }
}
int main(void){
    scanf("%d",&n);
    for(int i=0;i<2*n;i++){
        for(int j=0;j<2*n;j++){
            scanf("%lld",&g[i][j]);
        }
    }
    dfs(0,0);
    printf("%lld\n",ans);
    return 0;
}

H Second Large Rectangle

題意

給一個01矩陣,求第二大的全1子矩陣。

分析

和最大的全1子矩陣相似,對於每一行,咱們維護每一個點能往上延伸的最大長度,而後就至關於經典的單調棧題目,給一些不一樣高度的柱子,求最大面積。

咱們只須要用單調棧維護每一個高度做爲最小值能延伸到的最左和最右,而後掃一遍就能求出最大矩陣大小,可是這題要求的是次大,因此咱們不能單獨考慮最大的矩陣,也就是不能求出次大的最大子矩陣,而是要把每一個最大子矩陣長減1,和寬減1再加入更新。

代碼

#include <bits/stdc++.h>
using namespace std;
const int N=1050;
const int INF=0x3f3f3f3f;
int h[N],le[N],ri[N];
int n,m;
char s[N][N];
struct node{
    int l,r,h;
    bool operator <(const node &rhs)const{
        if(l!=rhs.l){
            return l<rhs.l;
        }else{
            if(r!=rhs.r){
                return r<rhs.r;
            }else{
                return h<rhs.h;
            }
        }
    }
};
map<node,int> mp;
stack<int> ss;
int mx,se;
void update(int x){
    if(x>=mx){
        se=mx;
        mx=x;
    }else if(x>se){
        se=x;
    }
}
int main(void){
    // freopen("in.txt","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%s",s[i]+1);
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(s[i][j]=='1'){
                h[j]++;
            }else{
                h[j]=0;
            }
        }
        while(!ss.empty()){
            ss.pop();
        }
        for(int j=1;j<=m;j++){
            while(ss.size()>0 && h[j]<=h[ss.top()]){
                ss.pop();
            }
            if(ss.size()>0){
                le[j]=ss.top()+1;
            }else{
                le[j]=1;
            }
            ss.push(j);
        }
        while(!ss.empty()){
            ss.pop();
        }
        for(int j=m;j>=1;j--){
            while(ss.size()>0 && h[j]<=h[ss.top()]){
                ss.pop();
            }
            if(ss.size()>0){
                ri[j]=ss.top()-1;;
            }else{
                ri[j]=m;
            }
            ss.push(j);
        }
        for(int j=1;j<=m;j++){
            if(h[j]==INF){
                continue;
            }
            if(mp[{le[j],ri[j],h[j]}]){
                continue;
            }
            mp[{le[j],ri[j],h[j]}]=1;
            update(h[j]*(ri[j]-le[j]+1));
            update(h[j]*(ri[j]-le[j]));
            update((h[j]-1)*(ri[j]-le[j]+1));
        }
    }
    printf("%d\n",se);
    return 0;
}
相關文章
相關標籤/搜索