您的位置:首页 > 大数据 > 人工智能

Hdu 4391 Paint The Wall

2013-08-05 11:41 411 查看
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4391

给出一个长度为n(n <= 10 ^ 5)每个点标有颜色的数列,现有如下两种操作,操作总数不大于10 ^ 5:

1、C l r z 将[l, r]区间内所有点的颜色标为z。

2、Q l r z 查询[l, r]区间内颜色为z的点的数目。

本题可以用线段树+剪枝优化做。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;

#define Maxn 100005
#define lx (x<<1)
#define rx ((x<<1) | 1)
#define MID ((l + r)>>1)

//既充当标记,又充当值
int S[Maxn<<2];
//剪枝用
int mi[Maxn<<2],mx[Maxn<<2];
int A[Maxn];

int n,m;

void pushUp(int x)
{
S[x] = S[lx] == S[rx] ? S[lx] : -1;
mi[x] = min(mi[lx],mi[rx]);
mx[x] = max(mx[lx],mx[rx]);
}
void pushDown(int l,int r,int x)
{
if(S[x]!=-1)
{
S[rx] = S[lx] = S[x];
mi[rx] = mi[lx] = S[x];
mx[rx] = mx[lx] = S[x];
}
}
void build(int l,int r,int x)
{
if(l == r)
{
S[x] = mi[x] = mx[x] = A[l];
return;
}
build(l,MID,lx);
build(MID+1,r,rx);
pushUp(x);
}

void update(int L,int R,int d,int l,int r,int x)
{
if(S[x] == d) return;
if(L<=l && r<=R)
{
S[x] = mi[x] = mx[x] = d;
return;
}
pushDown(l,r,x);
if(L<=MID) update(L,R,d,l,MID,lx);
if(MID+1<=R) update(L,R,d,MID+1,r,rx);
pushUp(x);
}
int query(int L,int R,int z,int l,int r,int x)
{
if(L<=l && r<=R)
{
if(z>=mi[x] && z<=mx[x])
{
if(S[x]!=-1)
{
if(S[x] == z) return r-l+1;
else return 0;
}
else return query(L,R,z,l,MID,lx) + query(L,R,z,MID+1,r,rx);
}
else return 0;
}
pushDown(l,r,x);
int ans = 0;
if(L<=MID) ans += query(L,R,z,l,MID,lx);
if(MID+1<=R) ans += query(L,R,z,MID+1,r,rx);
return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int op,a,b,z;
while(scanf(" %d %d",&n,&m)!=EOF)
{
for(int i=1;i<=n;i++)
{
scanf(" %d",&A[i]);
}
build(1,n,1);
for(int i=0;i<m;i++)
{
scanf(" %d %d %d %d",&op,&a,&b,&z);
a++;b++;
if(op == 1) update(a,b,z,1,n,1);
else if(op == 2)
{
int ans = query(a,b,z,1,n,1);
printf("%d\n",ans);
}
}
}
return 0;
}


另一种很好的做法是分块哈希。

将直线分成sqrt(n)段,每段用一个hash表存储,然后将每次修改时,如果是部分修改,则在这段内遍历修改,然后重新生成hash表(此时注意标记的下放)。如果是整段修改,则给这块设置一个标记,标记为这一整段都被涂成该色,查询的方法和修改相同。因为任意一个线段能分成 :

i*sqrt(n) -1 -> L,(i+1)*(sqrt)-1 -> i*sqrt(n),….r -> j*sqrt(n).这样的小于等于sqrt(n)的段。除了两头需要暴力查询及修改需要O(sqrt(n))外,其他部分都只用O(1)所以每次操作复杂度为O(sqrt(n))

Hash用Map来模拟。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
#include <map>
using namespace std;

#define Maxn 100010
#define BLOCK 510
int n,m;
int A[Maxn];

//分块相关
int block_num;
int block_size;
map<int ,int> hash[BLOCK];
int color[BLOCK];

void updateOne(int blockNo)
{
hash[blockNo].clear();
color[blockNo] = -1;
int st = blockNo*block_size;
int ed = st + block_size;
if(ed>n) ed = n;
for(int j = st;j<ed;j++)
{
hash[blockNo][A[j]]++;
}
}
void initHash()
{
block_size = (int)(sqrt(n));
block_num = n/block_size + (n%block_size ? 1:0);
for(int i=0;i<block_num;i++)
{
updateOne(i);
}
}

//标记下放
void pushDown(int blockNo)
{
if(color[blockNo]!=-1)
{
int st = blockNo*block_size;
int ed = st + block_size;
if(ed>n) ed = n;
for(int i = st; i<ed;i++)
{
A[i] = color[blockNo];
}
color[blockNo] = -1;
}
}
void update(int l,int r,int z)
{
int lBlockNum = l/block_size;
int lBlockSt = l%block_size;
int rBlockNum = r/block_size;
int rBlockSt = r%block_size;
//在同一块
if(lBlockNum == rBlockNum)
{
//未完全覆盖则暴力修改整个块
//结束点在末尾算作完全覆盖的情况
if((rBlockSt - lBlockSt + 1)!=block_size && r!=n-1)
{
pushDown(lBlockNum);
for(int i=l;i<=r;i++) A[i] = z;
updateOne(lBlockNum);
}
//完全覆盖染色
else color[lBlockNum] = z;
}
else
{
//未完全覆盖则暴力修改两个块,完全覆盖则染色
if(lBlockSt!=0)
{
pushDown(lBlockNum);
for(int i=l;i<(lBlockNum+1)*block_size;i++) A[i] = z;
updateOne(lBlockNum);
}
else color[lBlockNum] = z;
if(rBlockSt!=block_size-1 && r!=n-1)
{
pushDown(rBlockNum);
for(int i=rBlockNum*block_size;i<=r;i++) A[i] = z;
updateOne(rBlockNum);
}
else color[rBlockNum] = z;
for(int i=lBlockNum+1;i<rBlockNum;i++)
{
color[i] = z;
}
}
}
int getOne(int l,int r,int z)
{
int ans = 0;
for(int i=l;i<=r;i++)
{
if(A[i] == z) ans++;
}
return ans;
}
int query(int l,int r,int z)
{
int ans = 0;
int lBlockNum = l/block_size;
int lBlockSt = l%block_size;
int rBlockNum = r/block_size;
int rBlockSt = r%block_size;
//同一块
if(lBlockNum == rBlockNum)
{
if(color[lBlockNum] == z) ans += rBlockSt - lBlockSt + 1;
else if(color[lBlockNum] == -1) ans += getOne(l,r,z);
}
else
{
if(color[lBlockNum] == z) ans += (lBlockNum + 1)*block_size - l;
else if(color[lBlockNum] == -1) ans += getOne(l,(lBlockNum + 1)*block_size - 1,z);
if(color[rBlockNum] == z) ans += r - rBlockNum* block_size + 1;
else if(color[rBlockNum] == -1) ans += getOne(rBlockNum*block_size,r,z);
for(int i=lBlockNum+1;i<rBlockNum;i++)
{
if(color[i] == z) ans += block_size;
else if(color[i] == -1 && hash[i].find(z)!=hash[i].end()) ans += hash[i][z];
}
}
return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int op,a,b,z;
while(scanf(" %d %d",&n,&m)!=EOF)
{
for(int i=0;i<n;i++) scanf(" %d",&A[i]);
initHash();
for(int i=0;i<m;i++)
{
scanf(" %d %d %d %d",&op,&a,&b,&z);
if(op == 1)
{
update(a,b,z);
}
else if(op == 2)
{
int ans = query(a,b,z);
//int ans = 0;
printf("%d\n",ans);
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: