zoukankan      html  css  js  c++  java
  • Codeforces Round #463

    A - Palindromic Supersequence

    /*
        题目大意:给出一个串,构造一个包含该串的回文串
    */
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    const int N=10010;
    char s[N];
    int main(){
        scanf("%s",s);
        int n=strlen(s);
        for(int i=n-1;i;i--)printf("%c",s[i]);
        for(int i=0;i<n;i++)printf("%c",s[i]);
        puts(""); return 0;
    }

    B - Recursive Queries

    /*
        题目大意:定义F函数为一个数所有数字的乘积,定义G函数为分段函数,
        当x小于10时为其本身,否则G(x)=G(F(x)),求区间有多少数G函数值为k
    */
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    int T,l,r,k,sum[10][1000010],f[1000010];
    int cal(int x){
        int ans=1;
        while(x)ans=x%10?ans*(x%10):ans,x/=10;
        return ans;
    }
    int main(){
        for(int i=0;i<10;i++)f[i]=i;
        for(int i=10;i<=1000000;i++)f[i]=f[cal(i)];
        for(int i=1;i<=1000000;i++){
            for(int j=0;j<10;j++)sum[j][i]=sum[j][i-1];
            sum[f[i]][i]++;
        }
        scanf("%d",&T);
        while(T--){
            scanf("%d%d%d",&l,&r,&k);
            printf("%d
    ",sum[k][r]-sum[k][l-1]);
        }
        return 0;
    }

    C - Permutation Cycle

    /*
        题目大意:求一个排列,使得其能被分解为大小为a或者b的置换
    */
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    int n,a,b,p=1;
    int main(){
        scanf("%d%d%d",&n,&a,&b);
        for(int i=0;i*a<=n;i++){  // !!!注意循环从0开始
            if((n-i*a)%b==0){
                int A=i,B=(n-i*a)/b;
                for(int j=1;j<=A;j++){
                    for(int k=1;k<a;k++)printf("%d ",p+k);
                    printf("%d ",p); p+=a;
                }
                for(int j=1;j<=B;j++){
                    for(int k=1;k<b;k++)printf("%d ",p+k);
                    printf("%d ",p); p+=b;
                }
                return 0;
            }
        }puts("-1");
        return 0;
    }

    D - Tree

    /*
        题目大意:开始给出只有一个节点的树,要求维护两个操作:
            1.往树上增加一个叶节点,连在R上,且质量为W
            2.寻找一个从R开始的最长数列满足,质量和不超过X,所有项为R的祖先,
            对于数列中相邻的项i,j,后项i为前项j的祖先,有w[i]大于w[j]
            并且在i到j的路径中不存在大于w[j]的点
        题解:考虑到下一项是跳到祖先链上第一个比其大的位置,因此我们每次插入节点的时候,
        直接将其插入到祖先链上第一个比其大的位置,查询的时候直接查询总和不超过X的最长链即可,
        因为祖先的质量单调递增,总质量和单调递增,我们可以在树上倍增查找
    */
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    const int N=400010,K=20;
    int tot;
    LL w[N],f[N][K],W[N][K];
    void link(int x,int y){
        f[x][0]=y;
        W[x][0]=w[x];
        for(int k=0;k<K-1;k++){
            y=f[x][k];
            if(y==-1)continue;
            f[x][k+1]=f[y][k];
            W[x][k+1]=W[x][k]+W[y][k];
        }
    }
    int sf(int x,LL wx){
        if(w[x]>=wx)return x;
        for(int k=K-1;~k;k--){
            if(f[x][k]<=0)continue;
            if(w[f[x][k]]<wx)x=f[x][k];
        }return f[x][0];
    }
    void init(){
        for(int i=0;i<N;i++)
            for(int k=0;k<K;k++)f[i][k]=-1;
        f[tot=1][0]=0;
    }
    LL query(int x,LL wx){
        LL res=0;
        for(int k=K-1;~k;k--){
            if(f[x][k]==-1||W[x][k]>wx)continue;
            wx-=W[x][k]; res+=1<<k;
            x=f[x][k];
        }return res;
    }
    int q,op;
    LL R,X,ans=0;
    int main(){
        init();
        scanf("%d",&q);
        while(q--){
            scanf("%d%lld%lld",&op,&R,&X);
            R^=ans,X^=ans;
            if(op==1){
                w[++tot]=X;
                link(tot,sf(R,X));
            }else printf("%lld
    ",ans=query(R,X)); 
        }
        return 0;
    }

    E - Team Work

    /*
        题目大意:求ΣC(n,i)*i^k,n<=10^9,k<=5000
        题解:利用第二类斯特林数公式进行代换
        ΣC(n,i)*i^k=ΣC(n,i)*(ΣS(k,j)*P(i,j))
                   =ΣC(n,i)*(ΣS(k,j)*j!*C(i,j))
                   =ΣS(k,j)*j!*ΣC(n,i)*C(i,j)
                   =ΣS(k,j)*Σ(n!/((n-i)!*(i-j)!))
                   =ΣS(k,j)*P(n,j)*ΣC(n-j,n-i)
                   =ΣS(k,j)*P(n,j)*2^(n-j)
    */
    #include <algorithm>
    #include <cstdio>
    using namespace std;
    typedef long long LL;
    const int mod=1000000007;
    const int inv2=mod/2+1;
    int n,k,S[5010];
    int pow(LL a,int b,int m){LL t=1;for(;b;b>>=1,a=a*a%m)if(b&1)t=t*a%m;return t;}
    void CalS(){S[1]=1;for(int i=2;i<=k;i++)for(int j=i;j;j--)S[j]=(1LL*S[j]*j+S[j-1])%mod;}
    int main(){
        scanf("%d%d",&n,&k); CalS(); 
        int P=1,M=pow(2,n,mod),ans=0;
        for(int i=1;i<=k;i++){
            P=1LL*P*(n-i+1)%mod;
            M=1LL*M*inv2%mod;
            ans=(1LL*ans+1LL*S[i]*P%mod*(LL)M%mod)%mod;
        }printf("%d
    ",ans); 
        return 0;
    }

    F - Escape Through Leaf

    /*
        题目大意:给出一棵树,树的根为1,树上每个节点有两个值a和b,
        从一个节点i可以跳到子树上任意一个节点j,代价是a[i]*b[j],
        从一个点到达另一个节点的代价是图中所有代价之和,
        问每个节点跳到任意一个叶节点的代价最小为多少。
        题解:我们递归计算答案,对于每个节点来说,他的答案为min(a[i]*b[j]+ans[j]),
        那么我们对于每个节点维护一个子树的动态凸包,求最大mx+b即可
        为避免过多的插入和比较,我们把信息都保存在节点最大子树的代表节点u[x]中,
        通过只改变标记u[x]以替代信息的大量转移。
    */
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const LL is_query=-(1LL<<62);
    struct Line{
        LL m,b;
        mutable function<const Line *()> succ;
        bool operator<(const Line &rhs)const{
            if(rhs.b!=is_query)return m<rhs.m;
            const Line *s=succ();
            if(!s)return 0;
            LL x=rhs.m;
            return b-s->b<(s->m-m)*x;
        }
    };
    struct HullDynamic:public multiset<Line>{
        bool check(iterator y) {
            auto z=next(y);
            if(y==begin()){
                if(z==end())return 0;
                return y->m==z->m&&y->b<=z->b;
            }
            auto x=prev(y);
            if(z==end())return y->m==x->m&&y->b<=x->b;
            return (x->b-y->b)*(long double)(z->m-y->m)>=(y->b-z->b)*(long double)(y->m-x->m);
        }
        void insert_line(LL m,LL b){
            auto y=insert({m,b});
            y->succ=[=]{return next(y)==end()?0:&*next(y);};
            if(check(y)){erase(y);return;}
            while(next(y)!=end()&&check(next(y)))erase(next(y));
            while(y!=begin()&&check(prev(y)))erase(prev(y));
        }
        LL query(LL x){
            auto l=*lower_bound((Line){x,is_query});
            return l.m*x+l.b;
        }
    };
    const int N=100010;
    int n,a[N],b[N],size[N],u[N],res[N],st[N],en[N];
    vector<int> g[N];
    LL ans[N];
    HullDynamic T[N];
    int dfn=0;
    void dfs(int x,int fx){
        st[x]=++dfn;
        res[dfn]=x;
        int t=-1; size[x]=1;
        for(int y:g[x]){
            if(y==fx)continue;
            dfs(y,x);
            size[x]+=size[y];
            if(t==-1||size[y]>size[t])t=y;
        }
        if(t==-1){
            T[x].insert_line(-b[x],0);
            ans[x]=0;
            u[x]=x;
        }
        else{
            u[x]=u[t];
            for(int y:g[x])if(y!=t){
                for(int i=st[y];i<=en[y];i++)T[u[x]].insert_line(-b[res[i]],-ans[res[i]]);
            }
            ans[x]=-T[u[x]].query(a[x]);
            T[u[x]].insert_line(-b[x],-ans[x]);
        }en[x]=dfn;
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",a+i);
        for(int i=1;i<=n;i++)scanf("%d",b+i);
        for(int i=1;i<n;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            g[x].push_back(y);
            g[y].push_back(x);
        }dfs(1,0);
        for(int i=1;i<=n;i++)printf("%lld ",ans[i]);
        return 0;
    }

    G - Palindrome Partition

    /*
        题目大意:给出一个串s,现在要求将其划分子串,并且划分结果呈回文
        求方案数,如abcdabcd划分为ab,cd,cd,ab,为回文。
        题解:我们将串后半部分倒序依次插入前半部分的后面,比如abcdcdab,
        将构成abbacddc,那么问题就转化为新串能拆分成回文子串的方案数,
        我们对新串边构建回文自动机,并在构建的同时计算答案,
        diff数组表示节点与其失配位置最长后缀回文的差值,
        记anc为将连续相同差值去除后的祖先节点,比如abbabbabba,
        在去除连续相同差值abb之后,得到祖先节点a,
        则对于一种差值长度的答案来说,若失配位置为其祖先节点,则答案等于失配节点的答案
        否则其值为拿掉祖先节点加一倍差值之后的位置的答案ans,加上其失配节点的答案g,
        对于不同差值长度的节点答案g累积到当前位置答案ans上去,
        最后输出结尾位置的答案即可。
    */
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    const int N=1000010;
    const int mod=1000000007; 
    int n,cnt,lst,s[N],l[N],f[N],diff[N],anc[N],g[N],ans[N],a[N][26];
    char S[N];
    void add(int &x,int y){if((x+=y)>=mod)x-=mod;}
    void init(){lst=cnt=1;f[0]=1,f[1]=0;l[1]=-1;} 
    void extend(int np,int c){
        int p=lst;
        while(s[np]!=s[np-l[p]-1])p=f[p];
        if(!a[p][c]){
            int x=++cnt,fp=f[p];
            while(s[np]!=s[np-l[fp]-1])fp=f[fp];
            l[x]=l[p]+2;
            f[x]=a[fp][c];
            a[p][c]=x;
            diff[x]=l[x]-l[f[x]];
            anc[x]=diff[x]==diff[f[x]]?anc[f[x]]:f[x];
        }lst=a[p][c];
    }
    int main(){
        scanf("%s",S+1);
        n=strlen(S+1);
        for(int i=1;i*2<=n;i++){
            s[i*2-1]=S[i]-'a';
            s[i*2]=S[n-i+1]-'a';
        }s[0]=-1; ans[0]=1; init(); 
        for(int i=1;i<=n;i++){
            extend(i,s[i]);
            for(int x=lst;x;x=anc[x]){
                g[x]=ans[i-l[anc[x]]-diff[x]];
                if(anc[x]!=f[x])add(g[x],g[f[x]]);
                if(i%2==0)add(ans[i],g[x]);
            }
        }printf("%d
    ",ans[n]);
        return 0;
    }
  • 相关阅读:
    漫谈PID——实现与调参
    匿名上位机的使用(51版)
    #if 和 #ifdef 条件编译注意
    关于HC04超声波模块测距的思考(51版)
    关于C语言文件操作
    C语言实现简易2048小游戏
    关于OELD屏显示电池电量的简易方法
    使用notepad++作为keil的外部编辑器
    2017年个人总结
    数据存入文件
  • 原文地址:https://www.cnblogs.com/forever97/p/cf463.html
Copyright © 2011-2022 走看看