• 【几个区间问题】


    大概是需要前缀和优化DP,和记录左右范围。

    还有一道题,没有下手,等做完了,再来总结。

    1,数组分拆: 给定数组,问有多少种拆分法,使得每一段和不为0。 (1e5)

     (用map优化DP)

    #include<map>
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int Mod=1e9+7;
    const int maxn=100010;
    map<int,int>mp;
    int dp[maxn],sum[maxn],Now=1;
    int main()
    {
        int n,i,j;
        Now=1; mp[0]=1;
        scanf("%d",&n);
        for(i=1;i<=n;i++) scanf("%d",&sum[i]),sum[i]+=sum[i-1];
        for(i=1;i<=n;i++){
            dp[i]=Now;
            int tmp=0;
            if(mp.find(sum[i])!=mp.end()) tmp=mp[sum[i]]%Mod;
            dp[i]=((dp[i]-tmp)%Mod+Mod)%Mod;
            Now=(Now+dp[i])%Mod;
            mp[sum[i]]=(mp[sum[i]]+dp[i])%Mod;
        }
        printf("%d
    ",dp[n]%Mod);
        return 0;
    }
    View Code

    2,数组区间: 求所有区间前k大的和。 (1e5,k<=50)

    (枚举每个数的的左右端点,我用的暴力,还可以优化)

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    const int maxn=2000010;
    ll ans,x[maxn],n,k,L[60],R[60];
    void getL(ll u)
    {
        memset(L,0,sizeof(L));
        ll tmp=1; L[tmp]=u;
        for(ll i=u-1;i>=1;i--){
            if(x[i]<x[u]) L[tmp]=i;
            else L[++tmp]=i;
            if(tmp>51) break;
        }
    }
    void getR(ll u)
    {
        memset(R,0,sizeof(R));
        ll tmp=1; R[tmp]=u;
        for(ll i=u+1;i<=n;i++){
            if(x[i]<x[u]) R[tmp]=i;
            else R[++tmp]=i;
            if(tmp>51) break;
        }
    }
    int main()
    {
        scanf("%lld%lld",&n,&k);
        for(ll i=1;i<=n;i++) scanf("%lld",&x[i]);
        for(ll i=1;i<=n;i++){
            getL(i);
            getR(i);
            for(ll j=1;j<=50&&L[j];j++)
             for(ll p=1;p<=50&&R[p];p++){
                 if(j+p-2<k){
                     ll tmp1=L[j-1],tmp2=R[p-1];
                     if(tmp1==0) tmp1=i+1;
                     if(tmp2==0) tmp2=i-1;
                     ans+=(ll)(tmp1-L[j])*(R[p]-tmp2)*x[i];
                 }
             }
        }
        
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

    3,数组分拆II:给定数组,问有多少种拆法,使得每一段不出现重复的数字,且要保证分组数最少。(1e5)

    (和第一题有些像,只要快速找到前面的最大限制即可)

    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=100010;
    const int Mod=1000000007;
    int a[maxn],d[maxn],dp[maxn],sum[maxn];
    int Laxt[maxn],pre[maxn],Fir[maxn],L,Now;
    int main()
    {
        int i,j,n;
        scanf("%d",&n);
        for(i=1;i<=n;i++){
           scanf("%d",&a[i]);
           if(Laxt[a[i]]) pre[i]=Laxt[a[i]];
           Laxt[a[i]]=i;
        }        
        for(i=1;i<=n;i++){
            if(pre[i]>L) L=pre[i];
            d[i]=d[L]+1;
            if(!Fir[d[i]]) Fir[d[i]]=i;
        }
        L=0; Now=0;
        for(i=1;d[i]==1;i++) dp[i]=1,sum[i]=i;
        for(;i<=n;i++){
            if(d[i]!=Now) {
                L=Fir[Now];
                Now=d[i];
            }
            L=max(L,pre[i]);
            dp[i]=((sum[Fir[Now]-1]-sum[L-1])%Mod+Mod)%Mod;
            sum[i]=(sum[i-1]+dp[i])%Mod;
        }
        printf("%d %d
    ",d[n],dp[n]);
        return 0;
    }
    View Code 

    4,有趣的子区间:给定a<=b求,问有多少对p,q,满足a<=p<=q<=b,使得区间[p,q]是有趣区间。有趣区间是指包含偶数个回文数。(1e10)

    5,区间价值: 区间的价值定理为有多少对相同元素,问所有区间里,第k小区间价值是多少。

    (利用单调性可以使用二分法,check的时候想双指针一样扫描)

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    const int maxn=200010;
    ll a[maxn],b[maxn],vis[maxn],cnt,N,K;
    bool check(ll x)
    {
        memset(vis,0,sizeof(vis));
        ll sum=0, ans=0;
        for(int tail=1,head=1;head<=N;head++){
            while(tail<=N&&sum+vis[a[tail]]<=x){
                sum+=vis[a[tail]];
                vis[a[tail]]++;
                tail++;
            }
            ans+=tail-head;
            if(ans>=K) return true;
            vis[a[head]]--;
            sum-=vis[a[head]];
        }
        return false;
    }
    int main()
    {
        ll T,L,R,Mid,ans;
        scanf("%lld",&T);
        while(T--){
            scanf("%lld%lld",&N,&K);
            for(int i=1;i<=N;i++){
                scanf("%d",&a[i]);
                b[i]=a[i];
            }
            sort(b+1,b+N+1);
            cnt=unique(b+1,b+N+1)-(b+1);
            for(int i=1;i<=N;i++) a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
            L=0;  R=N*(N-1)/2;
            while(L<=R){
                Mid=(L+R)/2;
                if(check(Mid)) ans=Mid,R=Mid-1;
                else L=Mid+1;
            }
            printf("%lld
    ",ans);
        } return 0;
    }
    View Code
  • 相关阅读:
    python 发包爬取中国移动充值页面---可判断手机号是否异常
    python利用selenium和safari浏览器驱动实现新浪微博自动点赞 Demo
    Django学习报错记录
    nginx和tomcat的区别
    Mac主机映射到域名
    mac下eclipse安装svn插件-subclipse
    移动端——等分,居中
    移动端——重置样式
    M端页面-绝对定位布局
    jquery-练习-折叠效果
  • 原文地址:https://www.cnblogs.com/hua-dong/p/8452988.html
Copyright © 2020-2023  润新知