• 2019杭电多校第二场


    6595 Everything Is Generated In Equal Probability

    题意

    给定一个(n),从([1,n])中等概率取出一个数,再等概率生成一个(n)的全排列,再计算这个全排列的函数值,求这个函数值的期望。

    函数表达为输入一个全排列,计算其逆序数,再等概率取出一个子序列(可以是空,可以是原序列),递归计算该子序列的函数值,累加返回。

    分析

    • 暴力求出样例输出对应的分数,发现前三个答案就是(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
    ",(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?'
    ':' ');
            }
        }
        return 0;
    }
    

    6600 Just Skip The Problem

    题意

    通过询问确定一个数(x),每次询问(y_i),回答(x&y_i)是否等于(y_i),问最少询问次数的方案数。

    分析

    • 显然是每次询问确定一位是最优的,可以通过计算期望证明。

    • 所以(x)(n)位数,就是有(n!)种不同的询问排列来确定这个数。

    • (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
    ");
                continue;
            }
            ll ans=1ll;
            for(ll i=2ll;i<=n;i++){
                ans=ans*i%mod;
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    

    6601 Keen On Everything But Triangle

    题意

    给n个棍子,多次询问区间内能组成三角形的最大周长。

    分析

    • 如果不考虑不能组成三角形的情况,那么答案就是区间内前三大的值之和。

    • 考虑有不能组合的情况,仍然是从大到小贪心地判断下一组能否组成三角形。

    • 长度在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
    ",ans);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    《图解HTTP》读书笔记
    【译】关于vertical-align你应知道的一切
    【移动端debug-5】可恶的1px万能实现方案
    《编写高质量代码改善JavaScript程序的188个建议》读书笔记
    【移动端debug-4】iOS下setTimeout无法触发focus事件的解决方案
    一张图看懂Function和Object的关系及简述instanceof运算符
    三张图搞懂JavaScript的原型对象与原型链
    一张图看懂encodeURI、encodeURIComponent、decodeURI、decodeURIComponent的区别
    图解call、apply、bind的异同及各种实战应用演示
    centos vm 桥接 --网络配置
  • 原文地址:https://www.cnblogs.com/zxcoder/p/11253099.html
Copyright © 2020-2023  润新知