2019杭電多校第二場

6595 Everything Is Generated In Equal Probability

題意

給定一個\(n\),從\([1,n]\)中等機率取出一個數,再等機率生成一個\(n\)的全排列,再計算這個全排列的函數值,求這個函數值的指望。node

函數表達爲輸入一個全排列,計算其逆序數,再等機率取出一個子序列(能夠是空,能夠是原序列),遞歸計算該子序列的函數值,累加返回。c++

分析

  • 暴力求出樣例輸出對應的分數,發現前三個答案就是\(\frac{0}{9},\)\(\frac{3}{9}\)\(\frac{8}{9}\),而後瞎猜幾個規律(\(\frac{n^2-1}{9}\))也許就過了...
  • 正解和證實:
    • 首先一個長度爲\(n\)的全排列,逆序數的指望應該是\(\frac{C_n^2}{2}\),即任取兩個數產生逆序數的指望爲\(\frac{1}{2}\)
    • 同時注意到全排列的子序列離散化以後也是一個全排列,所以這個遞歸的函數咱們能夠轉化爲遞推式子
    • \(f[i]\)表示取出數字爲\(i\)所獲得的函數值,所以\(f[i]=\frac{C_i^2}{2}+\frac{1}{2^i}\sum_{j=0}^iC_i^jf[j]\)
    • 左右邊都有\(f[i]\),將右邊拆開並移項,獲得\(f[i]=\frac{2^{i-1}}{2^i-1}\frac{C_i^2}{2}+\frac{1}{2^i-1}\sum_{j=0}^{i-1}C_i^jf[j]\)(網上的題解基本都是推錯的...官方題解也寫的很模糊...),而咱們要求的答案就是\(g[n]=\frac{1}{n}\sum_{i=1}^nf[i]\)
    • 根據\(f[i]\)的遞推應該能推出通項?不過感受最好的方法應該是根據樣例即\(g[1],g[2],g[3]\)推出接下來幾項找規律更容易。

代碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
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;
}
ll inv(ll x){
    return Pow(x,mod-2)%mod;
}
ll n;
int main(void){
    ll inv9=inv(9);
    while(~scanf("%lld",&n)){
        printf("%lld\n",(Pow(n,2)-1)*inv9%mod);
    }
    return 0;
}

6599 I Love Palindrome String

題意

給定一個字符串,詢問\(1-n\)每種長度的迴文子串有多少個,且必須知足前半部分也是迴文串。函數

分析

  • 迴文樹能夠求出每一個本質不一樣的迴文串長度和個數,且最多隻有\(len(S)\)個,而後枚舉每一個迴文子串,再判斷前半部分是不是迴文串。
  • 迴文串要判斷前半部分是否迴文,只需判斷前半部分和後半部分是否相等便可,能夠用字符串哈希。
  • 因此這是一道迴文樹模板題,能夠用來修正板子。

代碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned ull;
const int N=3e5+50;
const ull base=19260817;
int ans[N];
ull has[N],pw[N];
void initHash(){
    pw[0]=1;
    for(int i=1;i<N;i++){
        pw[i]=pw[i-1]*base;
    }
}
ull getHash(int l,int r){
    return has[r]-has[l-1]*pw[r-l+1];
}
//判斷迴文串[l,r],先後兩部分是否相等,如果,說明是迴文
bool check(int l,int r){
    int len=r-l+1;
    int mid=(l+r)/2;
    return getHash(l,mid)==getHash((len%2?mid:mid+1),r);
}
struct PT{
    int next[N][26],fail[N],cnt[N],num[N],len[N];
    int S[N],last,id[N],n,p;
    int newnode(int l){
        for(int i=0;i<26;i++){
            next[p][i]=0;
        }
        cnt[p]=num[p]=0;
        len[p]=l;
        return p++;
    }
    void init(){
        p=0;
        //先0再-1 這樣-1(p=1)的兒子就是0(p=0),後面0(p=0)的fail就是-1(p=1)
        newnode(0);
        newnode(-1);
        last=0;
        n=0;
        S[n]=-1;
        fail[0]=1;
    }
    int getFail(int x){
        while(S[n-len[x]-1]!=S[n]){
            x=fail[x];
        }
        return x;
    }
    void add(int c){
        c-='a';
        S[++n]=c;
        int cur=getFail(last);
        if(!next[cur][c]){
            int now=newnode(len[cur]+2);
            fail[now]=next[getFail(fail[cur])][c];
            num[now]=num[fail[now]]+1;
            next[cur][c]=now;
        }
        last=next[cur][c];
        cnt[last]++;
        id[last]=n;
    }
    void count(){
        for(int i=p-1;i>=0;i--){
            cnt[fail[i]]+=cnt[i];
        }
    }
    void getAns(){
        for(int i=2;i<p;i++){
            //枚舉每一個節點
            if(check(id[i]-len[i]+1,id[i])){
                ans[len[i]]+=cnt[i];
            }
        }
    }
}ac;
char s[N];
int main(void){
    // freopen("in.txt","r",stdin);
    initHash();
    while(~scanf("%s",s+1)){
        int n=strlen(s+1);
        ac.init();
        has[0]=0;
        for(int i=1;i<=n;i++){
            ac.add(s[i]);
            //這裏記得別寫成*10
            has[i]=has[i-1]*base+(s[i]-'a');
        }
        memset(ans,0,sizeof(ans));
        ac.count();
        ac.getAns();
        for(int i=1;i<=n;i++){
            printf("%d%c",ans[i],i==n?'\n':' ');
        }
    }
    return 0;
}

6600 Just Skip The Problem

題意

經過詢問肯定一個數\(x\),每次詢問\(y_i\),回答\(x\&y_i\)是否等於\(y_i\),問最少詢問次數的方案數。ui

分析

  • 顯然是每次詢問肯定一位是最優的,能夠經過計算指望證實。spa

  • 因此\(x\)\(n\)位數,就是有\(n!\)種不一樣的詢問排列來肯定這個數。code

  • \(n>1e6+3\)時,\(n!\)確定含有因子\(1e6+3\),所以結果爲0。遞歸

代碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e6+3;
ll n;
int main(void){
    while(~scanf("%lld",&n)){
        //n>=1e6+3 n!確定含有1e6+3這個因子,因此結果必定爲0
        if(n>=1000003){
            printf("0\n");
            continue;
        }
        ll ans=1ll;
        for(ll i=2ll;i<=n;i++){
            ans=ans*i%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

6601 Keen On Everything But Triangle

題意

給n個棍子,屢次詢問區間內能組成三角形的最大周長。ip

分析

  • 若是不考慮不能組成三角形的狀況,那麼答案就是區間內前三大的值之和。字符串

  • 考慮有不能組合的狀況,仍然是從大到小貪心地判斷下一組可否組成三角形。get

  • 長度在1e9範圍內,連續判斷的次數不會超過44次。

  • 證實就是斐波那契數列,假設連續的\(n\)個長度都不能組成三角形(1,1,10000000...這種確定能夠),假設長度增加很慢,每次都只是恰好前兩個木棍的和,那麼這就是斐波那契數列,而第45項斐波那契數列已經超過了1e9

  • 因此用主席樹查詢區間第k小。

代碼

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r)/2
typedef long long ll;
const int N=1e5+50;
int tr[N*30],sum[N*30],lr[N*30],rr[N*30];
int n,q,a[N],b[N],l,r;
int cnt;
int build(int l,int r){
    int rt=++cnt;
    sum[rt]=0;
    if(l==r){
        return rt;
    }
    lr[rt]=build(l,mid);
    rr[rt]=build(mid+1,r);
    return rt;
}
int update(int pre,int l,int r,int x){
    int rt=++cnt;
    lr[rt]=lr[pre];
    rr[rt]=rr[pre];
    sum[rt]=sum[pre]+1;
    if(l==r){
        return rt;
    }
    if(x<=mid){
        lr[rt]=update(lr[pre],l,mid,x);
    }else{
        rr[rt]=update(rr[pre],mid+1,r,x);
    }
    return rt;
}
int kth(int a,int b,int l,int r,int k){
    if(l>=r){
        return l;
    }
    int x=sum[lr[b]]-sum[lr[a]];
    if(k<=x){
        return kth(lr[a],lr[b],l,mid,k);
    }else{
        return kth(rr[a],rr[b],mid+1,r,k-x);
    }
}
int main(void){
    // freopen("in.txt","r",stdin);
    while(~scanf("%d%d",&n,&q)){
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        //權值建樹記得離散化
        sort(b+1,b+1+n);
        int m=unique(b+1,b+1+n)-b-1;
        //多組記得初始化
        cnt=0;
        //離散化後範圍記得是m
        tr[0]=build(1,m);
        for(int i=1;i<=n;i++){
            a[i]=lower_bound(b+1,b+1+m,a[i])-b;
            tr[i]=update(tr[i-1],1,m,a[i]);
        }
        while(q--){
            scanf("%d%d",&l,&r);
            int len=r-l+1;
            ll ans=-1;
            while(len>=3){
                int aa=b[kth(tr[l-1],tr[r],1,m,len)];
                int bb=b[kth(tr[l-1],tr[r],1,m,len-1)];
                int cc=b[kth(tr[l-1],tr[r],1,m,len-2)];
                if(aa>=bb+cc){
                    len--;
                    continue;
                }
                ans=1ll*aa+1ll*bb+1ll*cc;
                break;
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}
相關文章
相關標籤/搜索