HDU4973 A simple simulation problem
线段树分治
传送门:HustOJ
题意
设计数据结构,支持操作:
初始时给定n,表示序列123…n
D l r
:将序列下标lr之间的数复制一份,比如12345 D 2 4
,变为12233445,在D 1 4
变为112222333445。
Q l r
:查询下标lr之间的数中,出现次数最多的数的出现次数。
思路
是个线段树分治的题。
线段树开到n,每个叶子节点表示一个数,节点内存这个数出现了多少次。其余节点维护一个sum,表示他管控区间内共有多少个数,以及num,表示出现次数最多的数出现了多少次。
注意到区间可能不是整区间,比如122333445,我操作3,7。表示操作23334,2和4就不是一个完整的节点。这就是线段树分治的思路,将这个操作分开,分成前,中,后三部分。前后分别是一个单点操作,中是一个区间操作,可以lazy。
但是这又一个问题:如何将它给的实际序列下标信息转化成线段树的区间信息并分开前后?这需要写一个find函数:
int zhao(LL pos, LL &num, LL &tim, int l, int r, int rt)
/*
返回值是实际序列中pos位对应数
num是查询的pos位数字是实际序列中的第几个同样的数字
tim是该数字在实际序列中出现的总次数
*/
{
if(l==r)
{
num=pos;
tim=stree[rt].num;
return l;
}
pushdown(rt);
int m=(l+r)>>1;
if(stree[rt<<1].sum>=pos) return zhao(pos, num, tim, lson);
else return zhao(pos-stree[rt<<1].sum, num, tim, rson);
}
比如对于序列:12233334444567
我find10,返回值是4,num是3,tim是4。num作用在于在于单点加倍时,该点有一部分数字落在带加倍区间内,另一部分不落在。对于区间左端点,我肯定加倍tim-num+1个数字,右端点加倍num个数字。
返回值得作用在于扣去左右端点对应的数字。这样在区间加倍和区间查询时,直接查询[左侧返回值+1,右侧返回值-1]内。
恩。。。那个find那块比较不好理解,而且变量特别多,不要搞混。
另外。。这种多组数据的。。一定注意线段树要清干净。我刚开始疯狂wa,因为lazy没清干净,下一组数据查询时直接pushdown了>_<
另外据正雄大佬说,这题不用分治,完全单点加一个小优化就能过。不过这样有坑。
void db(LL L, LL R, int l, int r, int rt)
{
if(l==r)
{
stree[rt].sum+=(R-L+1);
stree[rt].num=stree[rt].sum;
return;
}
int m=(l+r)>>1;
if(R<=stree[rt<<1].sum) db(L, R, lson);
else if(L>stree[rt<<1].sum) db(L-stree[rt<<1].sum, R-stree[rt<<1].sum, rson);
else
{
LL cnm=stree[rt<<1].sum;
db(L, stree[rt<<1].sum, lson);
db(1, R-cnm, rson);
//坑!!!
//db(L, stree[rt<<1].sum, lson);
//db(1, R-stree[rt<<1].sum+1, rson);
}
pushup(rt);
}
如果按注释里面那么写,在第一个db完成后,左儿子的sum值就变了。R-他就不对了。如果没注意到这里,样例会输出2 2。
代码
线段树分治:注意线段树清干净!!,直接在外面置零就行
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <iomanip>
#define _ ios_base::sync_with_stdio(0),cin.tie(0)
#define M(a,b) memset(a,b,sizeof(a))
#define N n
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int MAXN=100007;
const int oo=0x3f3f3f3f;
typedef long long LL;
const LL loo=4223372036854775807ll;
typedef long double LB;
const LL mod=1e9+7;
const double eps=1e-6;
struct SegTree
{
LL sum; LL num;
int lazy;
}stree[4*MAXN];
void pushup(int rt)
{
stree[rt].sum=stree[rt<<1].sum+stree[rt<<1|1].sum;
stree[rt].num=max(stree[rt<<1].num, stree[rt<<1|1].num);
}
void pushdown(int rt)
{
if(stree[rt].lazy)
{
stree[rt<<1].lazy+=stree[rt].lazy;
stree[rt<<1|1].lazy+=stree[rt].lazy;
for(int i=0;i<stree[rt].lazy;i++)
{
stree[rt<<1].sum*=2;
stree[rt<<1].num*=2;
stree[rt<<1|1].sum*=2;
stree[rt<<1|1].num*=2;
}
stree[rt].lazy=0;
}
}
void build(int l, int r, int rt)
{
stree[rt].lazy=stree[rt].num=stree[rt].sum=0;
//注意区间外清空!!!否则非叶子节点就有了lazy
if(l==r)
{
stree[rt].num=1;
stree[rt].sum=1;
stree[rt].lazy=0;
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
pushup(rt);
}
void update1(int pos, LL num, int l, int r, int rt)//单点更新
{
if(l==r)
{
stree[rt].sum+=num;
stree[rt].num+=num;
return;
}
pushdown(rt);
int m=(l+r)>>1;
if(pos<=m) update1(pos, num, lson);
else update1(pos, num, rson);
pushup(rt);
}
void update2(LL L, LL R, int l, int r, int rt)//区间更新
{
if(L<=l&&r<=R)
{
stree[rt].lazy++;
stree[rt].num*=2;
stree[rt].sum*=2;
return;
}
pushdown(rt);
int m=(l+r)>>1;
if(L<=m) update2(L, R, lson);
if(R> m) update2(L, R, rson);
pushup(rt);
}
LL query(int L, int R, int l, int r, int rt)
{
if(L<=l&&r<=R)
{
return stree[rt].num;
}
pushdown(rt);
int m=(l+r)>>1;
LL ma=0;
if(L<=m) ma=max(ma, query(L, R, lson));
if(R> m) ma=max(ma, query(L, R, rson));
return ma;
}
int zhao(LL pos, LL &num, LL &tim, int l, int r, int rt)
/*
返回值是实际序列中pos位对应数
tim是该数字在实际序列中出现的总次数
num是查询的pos位数字是实际序列中的第几个同样的数字
*/
{
if(l==r)
{
num=pos;
tim=stree[rt].num;
return l;
}
pushdown(rt);
int m=(l+r)>>1;
if(stree[rt<<1].sum>=pos) return zhao(pos, num, tim, lson);
else return zhao(pos-stree[rt<<1].sum, num, tim, rson);
}
int main()
{
int T;scanf("%d", &T);
for(int t=0;t<T;t++)
{
printf("Case #%d:\n", t+1);
int n, q;scanf("%d%d", &n, &q);
build(1, n, 1);
for(int i=0;i<q;i++)
{
char c[2];LL l, r;
scanf("%s%lld%lld", c, &l, &r);
LL ln, rn;int lp, rp;LL tmp;
rp=zhao(r, rn, tmp, 1, n, 1);
lp=zhao(l, ln, tmp, 1, n, 1);
if(c[0]=='D')
{
if(lp==rp) update1(lp, rn-ln+1, 1, n, 1);
else
{
update1(rp, rn, 1, n, 1);
update1(lp, tmp-ln+1, 1, n, 1);
if(1<rp-lp)
update2(lp+1, rp-1, 1, n, 1);
}
}
else
{
if(lp==rp)
{
printf("%lld\n", rn-ln+1);
}
else
{
LL ans=0;
if(rn>ans) ans=rn;
if(tmp-ln+1>ans) ans=tmp-ln+1;
if(rp-lp>1)
{
ans=max(ans, query(lp+1, rp-1, 1, n, 1));
}
printf("%lld\n", ans);
}
}
}
}
return 0;
}
单点更新+蜜汁优化:注意两次更新之间的耦合关系!!
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <iomanip>
#define _ ios_base::sync_with_stdio(0),cin.tie(0)
#define M(a,b) memset(a,b,sizeof(a))
#define N n
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int MAXN=100007;
const int oo=0x3f3f3f3f;
typedef long long LL;
const LL loo=4223372036854775807ll;
typedef long double LB;
const LL mod=1e9+7;
const double eps=1e-6;
struct SegTree
{
LL sum; LL num;
}stree[4*MAXN];
void pushup(int rt)
{
stree[rt].sum=stree[rt<<1].sum+stree[rt<<1|1].sum;
stree[rt].num=max(stree[rt<<1].num, stree[rt<<1|1].num);
}
void build(int l, int r, int rt)
{
if(l==r)
{
stree[rt].num=1;
stree[rt].sum=1;
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
pushup(rt);
}
void db(LL L, LL R, int l, int r, int rt)
{
if(l==r)
{
stree[rt].sum+=(R-L+1);
stree[rt].num=stree[rt].sum;
return;
}
int m=(l+r)>>1;
if(R<=stree[rt<<1].sum) db(L, R, lson);
else if(L>stree[rt<<1].sum) db(L-stree[rt<<1].sum, R-stree[rt<<1].sum, rson);
else
{
LL cnm=stree[rt<<1].sum;
db(L, stree[rt<<1].sum, lson);
db(1, R-cnm, rson);
}
pushup(rt);
}
LL query(LL L, LL R, int l, int r, int rt)
{
if(l==r)
{
return R-L+1;
}
if(L==1&&R==stree[rt].sum) return stree[rt].num;
int m=(l+r)>>1;
LL res=-1;
if(R<=stree[rt<<1].sum) return query(L, R, lson);
else if(L>stree[rt<<1].sum) return query(L-stree[rt<<1].sum, R-stree[rt<<1].sum, rson);
else
{
return max(query(L, stree[rt<<1].sum, lson),
query(1, R-stree[rt<<1].sum, rson));
}
}
int main()
{
int T;scanf("%d", &T);
for(int t=0;t<T;t++)
{
printf("Case #%d:\n", t+1);
int n, q;scanf("%d%d", &n, &q);
build(1, n, 1);
for(int i=0;i<q;i++)
{
char c;LL l, r;
scanf(" %c %lld %lld", &c, &l, &r);
if(c=='D')
{
db(l, r, 1, n, 1);
}
else
{
printf("%lld\n", query(l, r, 1, n, 1));
}
}
}
return 0;
}