分块:

分块大法好:
具体应用见例题。

一、A Simple Problem with Integers POJ - 3468:
You have N integers, A1, A2, … , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

Input
The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, … , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
“C a b c” means adding c to each of Aa, Aa+1, … , Ab. -10000 ≤ c ≤ 10000.
“Q a b” means querying the sum of Aa, Aa+1, … , Ab.

Output
You need to answer all Q commands in order. One answer in a line.

Sample Input
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
Sample Output
4
55
9
15
Hint
The sums may exceed the range of 32-bit integers.

题意:区间修改,区间查询。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<queue>
#define ll long long
#define llu unsigned ll
using namespace std;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int maxn=100100;
ll a[maxn],sum[maxn],add[maxn];
int L[maxn],R[maxn],pos[maxn];
int n,m,t;

void change(int l,int r,ll val)
{
    int p=pos[l],q=pos[r];
    if(p==q)
    {
        for(int i=l;i<=r;i++)
            a[i]+=val;
        sum[p]+=val*(r-l+1);
    }
    else
    {
        for(int i=p+1;i<=q-1;i++) add[i]+=val;
        for(int i=l;i<=R[p];i++) a[i]+=val;
        sum[p]+=val*(R[p]-l+1);
        for(int i=L[q];i<=r;i++) a[i]+=val;
        sum[q]+=val*(r-L[q]+1);
    }
}

ll ask(int l,int r)
{
    int p=pos[l],q=pos[r];
    ll ans=0;
    if(p==q)
    {
        for(int i=l;i<=r;i++)
            ans+=a[i];
        ans+=add[p]*(r-l+1);
    }
    else
    {
        for(int i=p+1;i<=q-1;i++)
            ans+=sum[i]+add[i]*(R[i]-L[i]+1);
        for(int i=l;i<=R[p];i++) ans+=a[i];
        ans+=add[p]*(R[p]-l+1);
        for(int i=L[q];i<=r;i++) ans+=a[i];
        ans+=add[q]*(r-L[q]+1);
    }
    return ans;
}

int main(void)
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1;i<=n;i++) add[i]=0,scanf("%lld",&a[i]);

        t=sqrt(n);
        for(int i=1;i<=t;i++)
        {
            L[i]=(i-1)*t+1;
            R[i]=i*t;
        }
        if(R[t]<n) t++,L[t]=R[t-1]+1,R[t]=n;

        for(int i=1;i<=t;i++)
        {
            for(int j=L[i];j<=R[i];j++)
                pos[j]=i,sum[i]+=a[j];
        }

        int x,y;
        ll z;
        char pos[20];

        for(int i=1;i<=m;i++)
        {
            scanf("%s",pos);
            if(pos[0]=='C')
            {
                scanf("%d%d%lld",&x,&y,&z);
                change(x,y,z);
            }
            else
            {
                scanf("%d%d",&x,&y);
                printf("%lld\n",ask(x,y));
            }
        }
        return 0;
    }
}

二、AcWing 249. 蒲公英:
在乡下的小路旁种着许多蒲公英,而我们的问题正是与这些蒲公英有关。

为了简化起见,我们把所有的蒲公英看成一个长度为 n 的序列a1,a2,…,an,其中ai为一个正整数,表示第 i 棵蒲公英的种类编号。

而每次询问一个区间 [l,r] ,你需要回答区间里出现次数最多的是哪种蒲公英,如果有若干种蒲公英出现次数相同,则输出种类编号最小的那个。

输入格式
第一行两个整数n,m,表示有 n 株蒲公英,m 次询问。

接下来一行 n 个空格隔开的整数ai,表示蒲公英的种类。

再接下来 m 行每行两个整数l0,r0,我们令上次询问的结果为 x(如果这是第一次询问,则 x=0)。

令l=(l0+x-1) mod n+1,r=(r0+x-1) mod n+1,如果l>r,则交换l,r。

最终的询问区间为[l,r]。

输出格式
输出 m 行。

每行一个整数,表示每次询问的结果。

数据范围
1≤n≤40000,
1≤m≤50000,
1≤ai≤109
输入样例:
6 3
1 2 3 2 1 2
1 5
3 6
1 5
输出样例:
1
2
1

题意:强制在线查询区间众数。
不小心把一个 i 打成 t 了 ,调了四五个小时。。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<queue>
#define ll long long
#define llu unsigned ll
using namespace std;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int maxn=40100;
int c[40][40][maxn],st[40],ed[40],a[maxn],b[maxn];
int f[40][40],d[40][40];
int n,m,t,ans=0,len,res,num,cnt;
int L,R;

void init(void)
{
    t=pow(n*1.0,1.0/3);
    if(t) len=n/t;
    for(int i=1;i<=t;i++) st[i]=(i-1)*len+1,ed[i]=i*len;
    if(ed[t]<n) st[t+1]=ed[t]+1,ed[++t]=n;

    sort(a+1,a+n+1);
    cnt=unique(a+1,a+n+1)-(a+1);
    for(int i=1;i<=n;i++)
        b[i]=lower_bound(a+1,a+cnt+1,b[i])-a;

    for(int i=1;i<=t;i++)
    {
        for(int j=i;j<=t;j++)
        {
            for(int k=st[i];k<=ed[j];k++) c[i][j][b[k]]++;
            for(int k=1;k<=cnt;k++)
                if(c[i][j][k]>f[i][j]||c[i][j][k]==f[i][j]&&k<d[i][j])
                    f[i][j]=c[i][j][k],d[i][j]=k;
        }
    }

}

inline void update(int i)
{
    c[L][R][b[i]]++;
    if(c[L][R][b[i]]>res||c[L][R][b[i]]==res&&b[i]<num) res=c[L][R][b[i]],num=b[i];
}


int ask(int x,int y)
{
    if(x>y) swap(x,y);
    int l=0,r=0;
    for(int i=1;i<=t;i++) if(x<=ed[i]) {l=i;break;}
    for(int i=t;i>=1;i--) if(y>=st[i]) {r=i;break;}
    if(l+1<=r-1) L=l+1,R=r-1;
    else L=R=0;

    res=f[L][R],num=d[L][R];

    if(l==r)
    {
        for(int i=x;i<=y;i++) update(i);
        for(int i=x;i<=y;i++) c[L][R][b[i]]--;
    }
    else
    {
        for(int i=x;i<=ed[l];i++) update(i);
        for(int i=st[r];i<=y;i++) update(i);
        for(int i=x;i<=ed[l];i++) c[L][R][b[i]]--;
        for(int i=st[r];i<=y;i++) c[L][R][b[i]]--;
    }
    return a[num];

}

int main(void)
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&b[i]),a[i]=b[i];

    init();

    int x,y;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        ans=ask((x+ans-1)%n+1,(y+ans-1)%n+1);
        printf("%d\n",ans);
    }
    return 0;
}

三、AcWing 250. 磁力块:
在一片广袤无垠的原野上,散落着N块磁石。

每个磁石的性质可以用一个五元组(x,y,m,p,r)描述,其中x,y表示其坐标,m是磁石的质量,p是磁力,r是吸引半径。

若磁石A与磁石B的距离不大于磁石A的吸引半径,并且磁石B的质量不大于磁石A的磁力,那么A可以吸引B。

小取酒带着一块自己的磁石L来到了这片原野的(x0,y0)处,我们可以视磁石L的坐标为(x0,y0)。

小取酒手持磁石L并保持原地不动,所有可以被L吸引的磁石将会被吸引过来。

在每个时刻,他可以选择更换任意一块自己已经获得的磁石(当然也可以是自己最初携带的L磁石)在(x0,y0)处吸引更多的磁石。

小取酒想知道,他最多能获得多少块磁石呢?

输入格式
第一行五个整数x0,y0,pL,rL,N,表示小取酒所在的位置,磁石L磁力、吸引半径和原野上散落磁石的个数。

接下来N行每行五个整数x,y,m,p,r,描述一块磁石的性质。

输出格式
输出一个整数,表示最多可以获得的散落磁石个数(不包含最初携带的磁石L)。

数据范围
1≤N≤250000,
−109≤x,y≤109,
1≤m,p,r≤109
输入样例:
0 0 5 10 5
5 4 7 11 5
-7 1 4 7 8
0 2 13 5 6
2 -3 9 3 4
13 5 1 9 9
输出样例:
3

分块大法好:
变量有点多,为了防止溢出和忘了强制转型,就都用了long long

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<queue>
#define ll long long
#define llu unsigned ll
using namespace std;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int maxn=250100;
ll nowx,nowy,pl,rl,n,x,y,m,p,r,t;
struct node
{
    ll d,r,m,p;
    node(){}
    node(ll aa,ll bb,ll cc,ll dd)
    {
        d=aa,r=bb,m=cc,p=dd;
    }
}a[maxn];

bool cmp_m(const node &a,const node &b)
{
    return a.m<b.m;
}

bool cmp_d(const node &a,const node &b)
{
    return a.d<b.d;
}

ll L[maxn],R[maxn],ans=0;
ll d[maxn];
bool ha[maxn];

int main(void)
{
    scanf("%lld%lld%lld%lld%lld",&nowx,&nowy,&pl,&rl,&n);
    a[0]=node(0,rl*rl,0,pl);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&p,&r);
        a[i]=node((x-nowx)*(x-nowx)+(y-nowy)*(y-nowy),r*r,m,p);
    }

    t=sqrt(n);
    ll len=n/t;
    sort(a+1,a+n+1,cmp_d);

    int cnt=0;
    for(int i=1;i<=n;i+=len)
    {
        ++cnt;
        L[cnt]=i,R[cnt]=min(n,i+len-1),d[cnt]=a[R[cnt]].d;
        sort(a+L[cnt],a+R[cnt]+1,cmp_m);
    }

    queue<ll>q;
    q.push(0);

    while(q.size())
    {
        int pm=q.front();

        q.pop();
        ans++;
        ll rad=a[pm].r;
        ll pp=a[pm].p;
        for(int i=1;i<=cnt;i++)
        {
            if(d[i]>rad)
            {
                for(int j=L[i];j<=R[i];j++)
                    if(!ha[j]&&a[j].d<=rad&&a[j].m<=pp) ha[j]=true,q.push(j);
                break;
            }
            while(L[i]<=R[i]&&a[L[i]].m<=pp)
            {
                if(!ha[L[i]]) q.push(L[i]),ha[L[i]]=true;
                L[i]++;
            }
        }
    }
    printf("%lld\n",ans-1);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值