• hdu 6480-6489 (2018 黑龙江省大学生程序设计竞赛)


    Problem A

    题意:

    给你一个串,问你这个串内有多少个只含有一种字符的子串

    分析:

    双指针扫描,每次向右扫描相同字符,设每次扫描到的长度为lenlen,最终答案为len(len+1)2frac{len*(len+1)}{2}

    代码:
    #include <bits/stdc++.h>
    #define maxn 100005
    using namespace std;
    char str[maxn];
    typedef long long ll;
    int main()
    {
        //freopen("in.txt","r",stdin);
        int t;
        scanf("%d",&t);
        while(t--){
            scanf("%s",str);
            int l=0,r=0;
            int len=strlen(str);
            ll res=0;
            while(l<len&&r<len){
                while(r<len&&str[l]==str[r]) r++;
                int tmp=(r-l);
                res+=1ll*tmp*(tmp+1)/2;
                l=r;
            }
            cout<<res<<endl;
        }
    }
    

    Problem B


    Problem C

    分析:

    从(0,y)到(x,0)的路径的方案数,由组合数学知道是C(x+y,x),所以总的方案数就是C(x1+y1,x1)*C(x2+y2,x2)。然后我们要减去重叠的方案,两条路径会相交,那么我们在相交点换一下各自的路径,就变成从(0,y1)到(x2,0)和从(0,y2)到(x1,0)的方案数,那么就是C(x1+y2,x1)*C(x2+y1,x2)。两者相减就是答案

    代码:
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=200005;
    const int mod=1e9+7;
    long long f[maxn],inv[maxn],finv[maxn];
    void init(){
        f[0]=finv[0]=inv[0]=inv[1]=1;
        for (int i=2;i<maxn;i++)
            inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        for (int i=1;i<maxn;i++){
            f[i]=f[i-1]*i%mod;
            finv[i]=finv[i-1]*inv[i]%mod;
        }
    }
    ll C(ll n,ll m){
        if (n<0||m<0||m>n) return 0;
        return f[n]*finv[m]%mod*finv[n-m]%mod;
    }
    int main()
    {
        init();
        int T;
        while(~scanf("%d",&T)){
            while (T--){
                ll x1,x2,y1,y2;
                scanf("%lld%lld%lld%lld",&x1,&x2,&y1,&y2);
                ll sum=C(x1+y1,x1)*C(x2+y2,x2)%mod;
                ll tmp=C(x1+y2,x1)*C(x2+y1,x2)%mod;
                ll ans=(sum-tmp+mod)%mod;
                printf("%lld
    ",ans);
            }
        }
        return 0;
    }
    

    Problem D

    题意:

    给你一个长度为nn的序列,以及mm个询问,每个询问给你一个区间[l,r][l,r],其中mxmx为区间最大值,minmin为区间最小值,现在问你在区间[l,r][l,r]中是否存在从minminmxmx中的所有的数

    分析:

    问题显然可以转化为求区间[l,r][l,r]中不同的数的个数cntcnt,如果mxmin=cntmx-min=cnt则证明符合条件,反之则不符合条件。

    故此时的问题就转化成区间不同的数的个数以及RMQ问题。

    故我们可以用莫队和ST表分别维护者两个东西。

    代码:
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=5000005;
    int dp[200005][20];
    int mm[200005];
    void initRMQ(int n,int b[]){
        mm[0]=-1;
        for(int i=1;i<=n;i++){
            mm[i]=((i&(i-1)==0))?mm[i-1]+1:mm[i-1];
            dp[i][0]=b[i];
        }
        for(int j=1;j<=mm[n];j++){
            for(int i=1;i+(1<<j)-1<=n;i++){
                dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
            }
        }
    }
    int rmq1(int x,int y){
        int k=mm[y-x+1];
        return max(dp[x][k],dp[y-(1<<k)+1][k]);
    }
    int rmq2(int x,int y){
        int k=mm[y-x+1];
        return min(dp[x][k],dp[y-(1<<k)+1][k]);
    }
    struct  Q{
        int l,r,id;
        int t;
        bool operator<(const Q &b)const{
            if(t==b.t) return  r<b.r;
            else return t<b.t;
        }
    }q[maxn];
    int a[maxn];
    int ans=0;
    unordered_map<int,int>cnt;
    void del(int x){
        if(cnt.count(a[x])) cnt[a[x]]--;
    }
    void add(int x){
        if(!cnt.count(a[x])) cnt[a[x]]++;
    }
    int Res[maxn];
    int n,m;
    int main()
    {
        //freopen("in.txt","r",stdin);
        int t;
        scanf("%d",&t);
        while(t--){
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;i++){
                scanf("%d",&a[i]);
            }
            cnt.clear();
            initRMQ(n,a);
            int len=sqrt(n);
            for(int i=1;i<=m;i++){
                scanf("%d%d",&q[i].l,&q[i].r);
                q[i].id=i;
                q[i].t=(q[i].l-1)/len+1;
            }
            sort(q+1,q+1+m);
            int l=1,r=0;
            for(int i=1;i<=m;i++) {
                while (l > q[i].l) add(l - 1), l--;
                while (l < q[i].l) del(l), l++;
                while (r < q[i].r) add(r + 1), r++;
                while (r < q[i].r) del(r), r--;
                int maxx=rmq1(q[i].l,q[i].r);
                int minn=rmq2(q[i].l,q[i].r);
                Res[q[i].id] = (maxx-minn+1)>cnt.size()?1:0;
            }
            for(int i=1;i<=m;i++){
                cout<<(Res[i]==1?"NO":"YES")<<endl;
            }
        }
        return 0;
    }
    

    Problem E

    温暖的签到题,判断一下nn是否整除mm就没了。


    Problem F

    题意:

    给你两个串str1,str2str_1,str_2,现在让你找到最大的ss,使得从str1str_1str2str_2分别获得一个长度为ss的子串后,最多有kk个字符不相同。

    分析:

    第一个串和第二个串各取一个子串,子串相匹配求其中最长的不同个数不超过k的子串长度,这个一共有多少匹配情况呢,我们可以固定第一个串,枚举第二个串的开头,用第一个串去直接匹配第二个串的开头到结尾的子串,这样就可以完整地匹配所有情况。因为如果在第一个串的中间取一个子串来匹配,他这个小情况,可以变成对应第一个串匹配第二个串的某一个开头,其中这种匹配情况下,最长的满足题意的子串长度。

    接下来就是求每一种匹配情况下的最优答案了,这个可以用dpdp来实现,dp[i]dp[i]表示当前匹配到第i位的最长长度,随便记录一下最长长度的不同个数,接下来当不同个数到k时我们要减去最远的一个不同的匹配,这个可以用队列记录一下位置,把dp[i]dp[i]减去最远和次最远之间的长度,最后对每一个dpdpmaxmax就是答案。时间复杂度O((n+m)2)mathcal{O}((n+m)^2)

    代码:
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=4007;
    char s1[maxn],s2[maxn];
    int dp[2][maxn][2];
    queue<int>a[maxn<<1];
    int main(){
       // freopen("in.txt","r",stdin);
        int t,n,m,k;
        while (~scanf("%d",&t)){
            while (t--){
                scanf("%d",&k);
                scanf("%s %s",s1+1,s2+1);
                n=strlen(s1+1),m=strlen(s2+1);
                for (int i=1;i<=n+m+1;i++)
                    while (!a[i].empty()) a[i].pop();
                for (int i=1;i<=m;i++) a[i].push(n+1);
                for (int i=m+1;i<=n+m+1;i++) a[i].push(n-i+m+1);
                for (int i=0;i<=1;i++) {
                    dp[i][m+1][0]=dp[i][m+1][1]=0;
                    dp[i][m][0]=dp[i][m][1]=0;
                }
                for (int i=0;i<=m+1;i++) {
                    dp[0][i][0]=dp[0][i][1]=dp[1][i][0]=dp[1][i][1]=0;
                }
                int ans=0;
                for (int i=n;i>=1;i--)
                    for (int j=m;j>=1;j--){
                        if (s1[i]==s2[j]){
                            dp[i%2][j][0]=dp[(i+1)%2][(j+1)][0]+1;
                            dp[i%2][j][1]=dp[(i+1)%2][(j+1)][1];
                        }else {
                            int x=j+n-i;
                            a[x].push(i);
                            dp[i%2][j][0]=dp[(i+1)%2][(j+1)][0]+1;
                            dp[i%2][j][1]=dp[(i+1)%2][(j+1)][1]+1;
                            if (dp[i%2][j][1]>k){
                                int tmp=a[x].front();a[x].pop();
                                dp[i%2][j][0]-=tmp-a[x].front();
                                dp[i%2][j][1]--;
                            }
                        }
                        ans=max(ans,dp[i%2][j][0]);
                    }
                printf("%d
    ",ans);
            }
        }
    }
    

    Problem G

    题意:

    给你一个长度为nn的数组,每次你都可以选取n1n-1个 数组,令他们都1-1,现在问你能否使他们都变成同一个数,如果可以,输出最小的操作次数。

    分析:

    正难则反,取n1n-1个数令他们减11,等价于每次取11个数令它加11,因此我们只需正着做,每次对一个数进行操作即可。

    代码:
    #include <bits/stdc++.h>
    #define maxn 100005
    using namespace std;
    typedef long long ll;
    ll a[maxn],ans[maxn];
    int main()
    {
        //freopen("in.txt","r",stdin);
        int t;
        while (~scanf("%d",&t)){
            while(t--){
                int n;
                scanf("%d",&n);
                for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
                sort(a+1,a+1+n);
                ll sum=0;
                for (int i=1;i<=n;i++) ans[i]=a[n]-a[i],sum+=ans[i];
                bool f=1;
                for (int i=1;i<=n;i++){
                    if (a[i]<=sum-ans[i]){
                        f=0;
                        break;
                    }
                }
                if (f) printf("%lld
    ",sum);
                else puts("-1");
            }
        }
        return 0;
    }
    

    Problem H

    初中物理题,记住几个公式:P=mvmathcal{P}=frac{m}{v}F=PgVF_{浮}=mathcal{P}gV_{排}就没了

    #include <bits/stdc++.h>
    #define maxn 100005
    using namespace std;
    typedef long long ll;
    double sum,l,p;
    int main()
    {
        int T;
        while(~scanf("%d",&T)){
    
            while(T--){
                int n;
                scanf("%d",&n);
                sum=0;
                for(int i=1;i<=n;i++){
                    scanf("%lf%lf",&l,&p);
                    if(p<1)sum+=l*l*l*p;
                    else sum+=l*l*l;
                }
                double s,h,v;
                scanf("%lf%lf%lf",&s,&h,&v);
                v+=sum;
                double ans=v/s;
                ans=min(h,ans);
                printf("%.2f
    ",ans);
            }
        }
    }
    
    

    Problem I


    Problem J

    温暖的签到,模拟一下即可。

    代码:
    #include <bits/stdc++.h>
    #define maxn 100005
    using namespace std;
    char str[maxn];
    typedef long long ll;
    int a[maxn],id[maxn];
    int main()
    {
        //freopen("in.txt","r",stdin);
        int t;
        while (~scanf("%d",&t)){
            while (t--){
                int n;
                scanf("%d",&n);
                for (int i=1;i<=n;i++){
                    scanf("%d",&a[i]);
                    id[a[i]]=i;
                }
                int ans=0;
                for (int i=1;i<=n;i++){
                    if (a[i]!=i){
                        ans++;
                        int tmp=a[i],idd=id[i];
                        swap(a[i],a[id[i]]);
                        swap(id[tmp],id[i]);
                    }
                }
    //            for (int i=1;i<=n;i++) cout<<a[i]<<' ';cout<<endl;
    //            for (int i=1;i<=n;i++) cout<<id[i]<<' ';cout<<endl;
                printf("%d
    ",ans);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    [ZROI 9.16模拟赛] Tutorial
    [ZROI 9.15模拟赛] Tutorial
    [USACO 2017 Open Gold] Tutorial
    [USACO 2017 Feb Gold] Tutorial
    [USACO 2017 Jan Gold] Tutorial
    [BZOJ 1208] 宠物收养所
    [BZOJ 1500] 维护序列
    多表查询
    sql查询语句查询顺序
    单表查询
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11007140.html
Copyright © 2020-2023  润新知