分块大法好:
具体应用见例题。
一、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;
}