• 尺取法+变通


    所谓尺取法,是枚举区间的时候的一种优化思想。

    尺取法通常适用于选取区间有一定规律,或者所选取的区间有一定的变化趋势的情况。

    一般题目的尺取法比较普通,所以以下例题所用尺取法将比较特别。

    尺取+逆元

    https://ac.nowcoder.com/acm/contest/3005/C

    l代表左端点,r代表右端点,当长度=k时,就比较答案,然后l++,r++,始终维持着长度=k,而因为是区间乘积,所以在l++、r++时,我们要把第 l 个元素除去,以及乘上第 r 个元素,而我们当前答案值已经是取模过后的数,所以为防止出现取模后为0的情况,把第 l 个元素除去要用到逆元的方式来处理。

    #include <bits/stdc++.h>
    #define debug frelimiten("r.txt","r",stdin)
    #define mp make_pair
    #define ri register int
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int maxn = 2e5+10;
    const int INF = 0x3f3f3f3f; 
    const int mod = 998244353;
    inline ll read(){ll s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;}
    ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;}
    int n,k,i,l,r;
    ll sum,a[maxn],ans;
    int main()
    {
        n=read(),k=read();
        for (i=1;i<=n;i++) a[i]=read();
        sum=1;
        l=r=1;
        ans=-INF;
        while (r<=n)
        {
            if (a[r])
            {
                sum=sum*a[r]%mod;
                if (r-l+1==k)
                {
                    ans=max(ans,sum);
                    sum=sum*qpow(a[l],mod-2)%mod;
                    l++;
                }
            }
            else
            {
                l=r+1;
                sum=1;
            }
            r++;
        }
        cout<<ans<<endl;
        return 0;
    }
    View Code

    尺取+线段树

    ICPC 2017 Asia Xian K. LOVER II

    在此题之前有道简单版的B题,方法只用到了尺取法

    #include <bits/stdc++.h>
    #define debug freopen("r.txt","r",stdin)
    #define mp make_pair
    #define ri register int
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int maxn = 2e5+5;
    const int INF = 0x3f3f3f3f; 
    const int mod = 998244353;
    inline ll read(){ll s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;}
    ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;}
    int t,i,l;
    ll n,k,a[maxn],b[maxn],ans;
    int main()
    {
        t=read();
        while (t--)
        {
            n=read(),k=read();
            for (i=1;i<=n;i++) a[i]=read();
            for (i=1;i<=n;i++) b[i]=read();
            sort(a+1,a+1+n);
            sort(b+1,b+1+n);
            l=n;
            ans=0;
            for (i=1;i<=n;i++)
            {
                if (b[l]+a[i]>=k)
                {
                    ans++;
                    l--;
                } 
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    View Code

    而这题因为有个区间询问的附加问题,所以要在尺取法的基础上加上线段树。

    怎么加呢?因为在最终的选择序列中,最少需要有 1 个男生与 a[1] 的和大于等于 k,有 2 个男生 与 a[2] 的 和大于等于 k,以此类推,所以我们把每位女生至少所能配对的男生数设为 -4,-3,-2,-1,意思是如果能全部匹配的话,那么,第1位女生一定会有4位男生的和大于等于 k,第2位女生一定会有3位男生的和大于等于 k……,所以当当前区间男生能和所有女生配对的话,那么所有值将会>=0。

    我们计算每一位男生做为开头,然后在这名男生之后的男生中,要选多少个才能符合题目条件,所以我们设 l 做为某位男生的开头位置,r做为某位男生的最终位置,而以这位男生做为开头时,至少选到要到第 r位男生才能符合条件。而我们选下一位男生,即第l+1位男生时,先消除第l位男生的影响,然后再判断当前所有是否全部配对,若不全配对,就加入下一位男生r+1,计算这位男生的影响,直到符合题目条件。

    #include <bits/stdc++.h>
    #define debug freopen("r.txt","r",stdin)
    #define mp make_pair
    #define ri register int
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int maxn = 1e6+10;
    const int INF = 0x3f3f3f3f; 
    const int mod = 1e9+7;
    inline ll read(){ll s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;}
    ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;}
    struct node
    {
        ll lazy,minn;    
    };
    node segment_tree[maxn*6];
    struct segment
    {
        void push_up(int num)
        {
            segment_tree[num].minn=min(segment_tree[num<<1].minn,segment_tree[num<<1|1].minn);
        }
        void init(int num,int l,int r)
        {
            segment_tree[num].lazy=0;
            if (l==r) 
            {
                segment_tree[num].minn=-l;
                return;
            }
            int mid=(l+r)/2;
            init(num<<1,l,mid);
            init(num<<1|1,mid+1,r);    
            push_up(num);
        }
        void push_down(int num)
        {
            if (segment_tree[num].lazy==0) return;
            segment_tree[num<<1].lazy+=segment_tree[num].lazy;
            segment_tree[num<<1|1].lazy+=segment_tree[num].lazy;
            segment_tree[num<<1].minn+=segment_tree[num].lazy;
            segment_tree[num<<1|1].minn+=segment_tree[num].lazy;
            segment_tree[num].lazy=0;
        }
        void upgrade(int num,int l,int r,int want_zuo,int want_you,ll val)
        {
            if (want_zuo<=l && r<=want_you)
            {
                segment_tree[num].minn+=val;
                segment_tree[num].lazy+=val;
                return;
            }
            int mid=(l+r)/2;
            push_down(num);
            if (want_zuo<=mid) upgrade(num<<1,l,mid,want_zuo,want_you, val);
            if (want_you>mid) upgrade(num<<1|1,mid+1,r,want_zuo,want_you,val);
            push_up(num);
        }
        ll query(int num,int l,int r,int want_zuo,int want_you)
        {
            if (want_zuo<=l && r<=want_you) return segment_tree[num].minn;
            push_down(num);
            ll ans=INF;
            int mid=(l+r)/2;
            if (want_zuo<=mid) ans=min(ans,query(num<<1,l,mid,want_zuo,want_you));
            if (want_you>mid) ans=min(ans,query(num<<1|1,mid+1,r,want_zuo,want_you));
            return ans;
        }
    }tree;
    int t,m,n,k,i,a[maxn],b[maxn],R[maxn],pos,q,l,r;
    int main()
    {
        t=read();
        while (t--)
        {
            n=read(),m=read(),k=read();
            for (i=1;i<=n;i++) a[i]=read();
            for (i=1;i<=m;i++) b[i]=read();
            sort(a+1,a+1+n);
            tree.init(1,1,n);
            for (i=1;i<=m;i++)
            {
                R[i]=R[i-1];
                while (segment_tree[1].minn<0)
                {
                    R[i]++;
                    if (R[i]>m) break;
                    pos=lower_bound(a+1,a+1+n,k-b[R[i]])-a;
                    if (pos<=n)
                        tree.upgrade(1,1,n,pos,n,1);
                }
                pos=lower_bound(a+1,a+1+n,k-b[i])-a;
                if (pos<=n) tree.upgrade(1,1,n,pos,n,-1);
            }
            q=read();
            while (q--)
            {
                l=read(),r=read();
                if (R[l]<=r) cout<<1<<endl;
                    else cout<<0<<endl;
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    [SDOI2011] 消防 (树的直径,尺取法)
    [HNOI2006]公路修建问题 (二分答案,并查集)
    P1875 佳佳的魔法药水 (最短路,DP)
    [SCOI2016] 背单词 (Trie 树,贪心)
    [USACO08DEC] 秘密消息Secret Message (Trie树)
    [HDU4745] Two Rabbits (区间DP)
    [HDU4362] Palindrome subsequence (区间DP)
    评价手心输入法
    软件工程个人作业12
    第12周进度条
  • 原文地址:https://www.cnblogs.com/Y-Knightqin/p/12920202.html
Copyright © 2020-2023  润新知