【舒老师的胡策】互测3.11(贪心+分块+找规律)

T1:

这里写图片描述
这里写图片描述

题解:

其实题目是非常简单的,但是思维局限在了需要加边来求【到结尾可以延伸最远的距离】,然后交卷的前10min想到了O(nlogn)dp,我脑子进水了?,倒过来作最长下降子序列
求出来dis[i]表示i到结尾可以延伸最远的距离之后,权值为第一关键字,位置为第二关键字排序,然后贪心的寻找序列就好了,复杂度O(qn)

代码:

#include <cstdio>
#include <algorithm>
using namespace std;
const int N=10005;
int w[N],dis[N],g[N];
struct po{int x,z;}p[N];
int cmp(po a,po b){return a.z<b.z || (a.z==b.z&&a.x<b.x);}
int main()
{
    freopen("misswalmart.in","r",stdin);
    freopen("misswalmart.out","w",stdout);
    int n;scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&w[i]);
    for (int i=1;i<=n;i++) p[i].x=i,p[i].z=w[i];
    sort(p+1,p+n+1,cmp);
    int maxx=0;
    for (int i=n;i>=1;i--)
    {
        int l=1,r=maxx;
        while (l<=r)
        {
            int mid=(l+r)>>1;
            if (w[i]>=g[mid]) r=mid-1;
            else l=mid+1;
        }
        g[r+1]=w[i];
        dis[i]=r+1;maxx=max(maxx,dis[i]);
    }
    int Q;scanf("%d",&Q);
    while (Q--)
    {
        int x;scanf("%d",&x);
        if (maxx<x) {printf("LXALWAYSMISS\n");continue;}
        int last=0;
        for (int i=1;i<=n;i++)
          if (dis[p[i].x]>=x && p[i].x>p[last].x && p[i].z>p[last].z) 
          {
            printf("%d ",p[i].z),x--,last=i;
            if (!x) break;
          }
        printf("\n");
    }
}

T2:

这里写图片描述
这里写图片描述

题解:

感谢rty dalao的分块做法~
我们发现这个问题麻烦的地方就在于对整块操作之后不能立刻得到单点的信息
其实是可以的!
我们维护三个数组:fir,sec,thi,表示一个块维护的三个信息
fir是这个块开始的位置要增加的数字
sec是这个块开始的位置对应的标记位置和
thi是常数差,因为我们可以发现一个区间内的点累加的是前缀和,如果这一位累加的是Σx,那么下一位就是Σx+1,thi记录的就是这个1
真抽象啊,画画图举举例子就好了
这里写图片描述
那么对于一个块来说,我们只要知道开始的位置,就可以O(1)知道任何位置的值,只需要预处理Σx的前缀和就行了
问题来了,我们如何叠加标记呢?来看例子
比如一个块以前有一个标记,我们要再加上一个标记
这里写图片描述
这里写图片描述
其实用后项-前项的算法比较科学,那么第三项-第二项是x+y+4,这个常数项明显是跟叠加标记的次数有关的;第一项和第二项差x+y+2,第一项和第三项差2(x+y)+6,这样想要直接算出块内某一项就是(这一项到首项的距离)* sec[i]+thi[i] *(前缀和第 距离 项)
其他的问题就迎刃而解啦
然而使用O(松)优化会快一些,比如不用%用减法

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long
using namespace std;
const int N=100005;
const int mod=1e9+7;
int pos[N],block;LL ans=0,sum[320],all[N],w[N],a[N],fir[320],sec[320],thi[320];
void change(int l,int r)
{
    int num=1,R=r-l+1;
    if (pos[l]==pos[r])
    {
        for (int i=l;i<=r;i++) w[i]=(w[i]+a[num])%mod,sum[pos[l]]=(sum[pos[l]]+a[num])%mod,num++; 
    }
    else
    {
        for (int i=l;i<=pos[l]*block;i++) w[i]=(w[i]+a[num])%mod,sum[pos[l]]=(sum[pos[l]]+a[num])%mod,num++;
        for (int i=r;i>=(pos[r]-1)*block+1;i--) w[i]=(w[i]+a[R])%mod,sum[pos[r]]=(sum[pos[r]]+a[R])%mod,R--;

        for (int i=pos[l]+1;i<pos[r];i++) 
        {
            sum[i]=(sum[i]+all[num+block-1]-all[num-1]+mod)%mod;
            fir[i]=(fir[i]+a[num])%mod;
            thi[i]++; sec[i]=(sec[i]+num)%mod;
            num+=block; 
        }
    }
}
LL qurry(int l,int r)
{
    ans=0;
    if (pos[l]==pos[r])
    {
        for (int i=l;i<=r;i++) 
        {
            LL z=0,jl;
            if (thi[pos[i]])
            {
            z=fir[pos[i]]%mod,jl=(i-((pos[i]-1)*block+1))%mod;
            z=(z+jl*sec[pos[i]]%mod)%mod;
            z=(z+a[jl]*thi[pos[i]]%mod)%mod;    
            }
            ans=(ans+z+w[i])%mod;
        }
    }
    else 
    {
        for (int i=l;i<=pos[l]*block;i++) 
        {
            LL z=0,jl;
            if (thi[pos[i]])
            {
            z=fir[pos[i]]%mod,jl=(i-((pos[i]-1)*block+1))%mod;
            z=(z+jl*sec[pos[i]]%mod)%mod;
            z=(z+a[jl]*thi[pos[i]]%mod)%mod;    
            }
            ans=(ans+z+w[i])%mod;
        }
        for (int i=r;i>=(pos[r]-1)*block+1;i--) 
        {
            LL z=0,jl;
            if (thi[pos[i]])
            {
            z=fir[pos[i]]%mod,jl=(i-((pos[i]-1)*block+1))%mod;
            z=(z+jl*sec[pos[i]]%mod)%mod;
            z=(z+a[jl]*thi[pos[i]]%mod)%mod;    
            }
            ans=(ans+z+w[i])%mod;
        }
        for (int i=pos[l]+1;i<pos[r];i++) ans=(ans+sum[i]%mod)%mod;
    }
    return ans;
}
int main()
{
    freopen("stillmiss.in","r",stdin);
    freopen("stillmiss.out","w",stdout);
    int n,type,q;
    scanf("%d%d%d",&n,&type,&q);
    for (int i=1;i<=n;i++) a[i]=(a[i-1]+(LL)i)%mod;
    for (int i=1;i<=n;i++) all[i]=(a[i]+all[i-1])%mod;
    block=sqrt(n);
    for (int i=1;i<=n;i++) pos[i]=(i-1)/block+1;
    while (q--)
    {
        int l,r;char st[10];
        scanf("%s",st);scanf("%d%d",&l,&r);
        if (type) l=(l+ans-1)%n+1,r=(r+ans-1)%n+1;
        if (l>r) swap(l,r);
        if (st[0]=='C') change(l,r);
        else printf("%lld\n",qurry(l,r));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值