HDU - 5306 Gorgeous Sequence
链接:http://acm.hdu.edu.cn/showproblem.php?pid=5306
题意:维护三种操作
- 0 l r x 对区间 [ l , r ] [l,r] [l,r] 取 m i n ( a i , x ) min(a_i,x) min(ai,x)
- 1 l r 查询区间 [ l , r ] [l,r] [l,r] 中的最大值
- 2 l r 查询区间 [ l , r ] [l,r] [l,r] 的和
思路:维护区间和 sum、最大值 v1 、次大值 v2、区间最大值的个数 cnt、延迟标记 lazy
- 区间更新时,当 v1<=x 时,直接返回。 v2<x<v1 时,更新当前区间。当 x<=v2 时,暴力做单点更新。(代码实现时,长度为 1 的区间 v1 设为原本的值,v2 设为 -1。只需要保证,更新的 x 值一定会比 v2 大就好了)
#include <bits/stdc++.h>
#define ls (rt<<1)
#define rs (rt<<1|1)
#define ll long long
using namespace std;
const int maxn=1e6+5;
int t,n,m;
int a[maxn];
ll sum[maxn<<2];
int v1[maxn<<2],v2[maxn<<2],lazy[maxn<<2],cnt[maxn<<2];
void pushUp(int rt)
{
sum[rt]=sum[ls]+sum[rs];
if(v1[ls]>v1[rs])
{
v1[rt]=v1[ls];
cnt[rt]=cnt[ls];
v2[rt]=max(v2[ls],v1[rs]);
}
else if(v1[ls]<v1[rs])
{
v1[rt]=v1[rs];
cnt[rt]=cnt[rs];
v2[rt]=max(v2[rs],v1[ls]);
}
else
{
v1[rt]=v1[ls];
cnt[rt]=cnt[ls]+cnt[rs];
v2[rt]=max(v2[ls],v2[rs]);
}
}
void solve(int rt,int val)
{
if(v1[rt]<=val) return;
sum[rt]-=1ll*cnt[rt]*(v1[rt]-val);
v1[rt]=lazy[rt]=val;
}
void pushDown(int rt)
{
if(lazy[rt]!=-1)
{
solve(ls,lazy[rt]);
solve(rs,lazy[rt]);
lazy[rt]=-1;
}
}
void build(int rt,int L,int R)
{
lazy[rt]=-1;
if(L==R)
{
sum[rt]=a[L];
v1[rt]=a[L],v2[rt]=-1,cnt[rt]=1;
return;
}
int mid=(L+R)>>1;
build(ls,L,mid);
build(rs,mid+1,R);
pushUp(rt);
}
void update(int rt,int l,int r,int L,int R,int val)
{
if(v1[rt]<=val) return;
if(l<=L&&R<=r&&v2[rt]<val)
{
solve(rt,val);
return;
}
pushDown(rt);
int mid=(L+R)>>1;
if(l<=mid) update(ls,l,r,L,mid,val);
if(r>mid) update(rs,l,r,mid+1,R,val);
pushUp(rt);
}
int queryMax(int rt,int l,int r,int L,int R)
{
if(l<=L&&R<=r) return v1[rt];
pushDown(rt);
int mid=(L+R)>>1;
int ans=0;
if(l<=mid) ans=max(ans,queryMax(ls,l,r,L,mid));
if(r>mid) ans=max(ans,queryMax(rs,l,r,mid+1,R));
return ans;
}
ll querySum(int rt,int l,int r,int L,int R)
{
if(l<=L&&R<=r) return sum[rt];
pushDown(rt);
int mid=(L+R)>>1;
ll ans=0;
if(l<=mid) ans+=querySum(ls,l,r,L,mid);
if(r>mid) ans+=querySum(rs,l,r,mid+1,R);
return ans;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; ++i) scanf("%d",&a[i]);
build(1,1,n);
while(m--)
{
int op,l,r,x;
scanf("%d%d%d",&op,&l,&r);
if(op==0)
{
scanf("%d",&x);
update(1,l,r,1,n,x);
}
else if(op==1) printf("%d\n",queryMax(1,l,r,1,n));
else printf("%lld\n",querySum(1,l,r,1,n));
}
}
return 0;
}
HDU - Art Class
链接:http://acm.hdu.edu.cn/showproblem.php?pid=6888
题意:在二维平面上,给出 n 个矩阵,每个矩阵用 ( l i , r i , h i ) (l_i,r_i,h_i) (li,ri,hi) 表示,表示将 [ l i , 0 ] 、 [ r i , h i ] [l_i,0] 、[r_i,h_i] [li,0]、[ri,hi] 这个矩阵染成黑色。每次加一个点,询问一次周长。 ( 1 ≤ n ≤ 2 × 1 0 5 1 ≤ l i ≤ r i ≤ 1 0 9 , 1 ≤ h i ≤ 1 0 9 ) (1\le n \le 2\times 10^5 1\le l_i \le r_i \le 10^9,1\le h_i \le 10^9) (1≤n≤2×1051≤li≤ri≤109,1≤hi≤109)
#include <bits/stdc++.h>
#define ls (rt<<1)
#define rs (rt<<1|1)
#define ll long long
using namespace std;
const int maxn=2e5+5;
int t,n;
struct Node
{
int l,r,h;
} opt[maxn];
int b[maxn<<1],tot=0;
ll sum1[maxn<<3],lazy1[maxn<<3];
ll sum2[maxn<<3],lazy2[maxn<<3];
int le[maxn<<3],ri[maxn<<3],mn1[maxn<<3],mn2[maxn<<3],cnt[maxn<<3];
void build(int rt,int L,int R)
{
sum1[rt]=lazy1[rt]=0;
sum2[rt]=lazy2[rt]=le[rt]=ri[rt]=mn1[rt]=cnt[rt]=0;
mn2[rt]=1e9+100;
if(L==R) return;
int mid=(L+R)>>1;
build(ls,L,mid);
build(rs,mid+1,R);
}
void pushUp1(int rt,int L,int R)
{
if(lazy1[rt]) sum1[rt]=b[R+1]-b[L];
else sum1[rt]=sum1[ls]+sum1[rs];
}
void update1(int rt,int l,int r,int L,int R)
{
if(lazy1[rt]) return;
if(l<=L&&R<=r)
{
sum1[rt]=b[R+1]-b[L];
lazy1[rt]=1;
return;
}
int mid=(L+R)>>1;
if(l<=mid) update1(ls,l,r,L,mid);
if(r>mid) update1(rs,l,r,mid+1,R);
pushUp1(rt,L,R);
}
void solve(int rt,int val)
{
if(val<=mn1[rt]) return;
sum2[rt]-=1ll*cnt[rt]*(val-mn1[rt]);
if(le[rt]==mn1[rt]) le[rt]=val;
if(ri[rt]==mn1[rt]) ri[rt]=val;
mn1[rt]=lazy2[rt]=val;
}
void pushDown2(int rt)
{
if(lazy2[rt])
{
solve(ls,lazy2[rt]);
solve(rs,lazy2[rt]);
lazy2[rt]=0;
}
}
void pushUp2(int rt)
{
sum2[rt]=sum2[ls]+sum2[rs]+abs(ri[ls]-le[rs]);
le[rt]=le[ls];
ri[rt]=ri[rs];
if(mn1[ls]<mn1[rs])
{
mn1[rt]=mn1[ls];
cnt[rt]=cnt[ls];
mn2[rt]=min(mn2[ls],mn1[rs]);
}
else if(mn1[ls]>mn1[rs])
{
mn1[rt]=mn1[rs];
cnt[rt]=cnt[rs];
mn2[rt]=min(mn1[ls],mn2[rs]);
}
else
{
mn1[rt]=mn1[rs];
cnt[rt]=cnt[ls]+cnt[rs];
mn2[rt]=min(mn2[ls],mn2[rs]);
}
if((ri[ls]==mn1[rt]||le[rs]==mn1[rt])&&ri[ls]!=le[rs]) cnt[rt]++;
}
void update2(int rt,int l,int r,int L,int R,int val)
{
if(mn1[rt]>=val) return;
if(l<=L&&R<=r&&val<mn2[rt])
{
solve(rt,val);
return;
}
pushDown2(rt);
int mid=(L+R)>>1;
if(l<=mid) update2(ls,l,r,L,mid,val);
if(r>mid) update2(rs,l,r,mid+1,R,val);
pushUp2(rt);
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
tot=0;
for(int i=1; i<=n; ++i)
{
int l,r,h;
scanf("%d%d%d",&l,&r,&h);
opt[i]= {l,r,h};
b[++tot]=l;
b[++tot]=r;
}
sort(b+1,b+1+tot);
tot=unique(b+1,b+1+tot)-b-1;
build(1,0,tot+1);
for(int i=1; i<=n; ++i)
{
int l=lower_bound(b+1,b+1+tot,opt[i].l)-b;
int r=lower_bound(b+1,b+1+tot,opt[i].r)-b-1;
update1(1,l,r,0,tot+1);
update2(1,l,r,0,tot+1,opt[i].h);
printf("%lld\n",sum1[1]*2+sum2[1]);
}
}
return 0;
}
J Just Another Game of Stones
题意:维护两种操作
- 1 l r x 对区间 [l,r] 的元素取 m a x ( a i , x ) max(a_i,x) max(ai,x)
- 2 l r x 对区间 [l,r] 的元素以及 x 做 nim 博弈,问有多少种胜利的取法。
思路:
- 对于操作一,直接用 segment tree beats 维护
- nim 博弈成立的条件是异或和大于 0 。等于 0 直接输出答案。首先取到区间 [l,r] 和 x 异或值,设为 a 。那么对于一堆数量为 b 的石子来说,它存在取法的条件就是:b>b xor a 。只有 b 大于 b,才能够从 b 中拿一些石子,使得 b 变成 b xor a。从而使得下一步的异或结果为 0 。
- 所以就是计数 满足 b>b xor a 这个条件的 b 的数量。假设 a 的最高位为 p ,可以发现只有 b 的第 p 位也为 1 ,才会满足条件。
- 所以最后就是查询区间内,第 p 位为 1 的个数以及区间的异或和
- 所以维护区间内,每一位的个数就好了。
#include <bits/stdc++.h>
#define ls (rt<<1)
#define rs (rt<<1|1)
#define ll long long
using namespace std;
const int maxn=2e5+5;
int n,q,a[maxn];
int xsum[maxn<<2],num[maxn<<2][31],mn1[maxn<<2],mn2[maxn<<2],cnt[maxn<<2];
void pushUp(int rt)
{
xsum[rt]=xsum[ls]^xsum[rs];
for(int i=0; i<=29; ++i)
num[rt][i]=num[ls][i]+num[rs][i];
if(mn1[ls]>mn1[rs])
{
mn1[rt]=mn1[rs];
cnt[rt]=cnt[rs];
mn2[rt]=min(mn1[ls],mn2[rs]);
}
else if(mn1[ls]<mn1[rs])
{
mn1[rt]=mn1[ls];
cnt[rt]=cnt[ls];
mn2[rt]=min(mn2[ls],mn1[rs]);
}
else
{
mn1[rt]=mn1[ls];
cnt[rt]=cnt[ls]+cnt[rs];
mn2[rt]=min(mn2[ls],mn2[rs]);
}
}
void add(int rt,int val)
{
if(mn1[rt]>=val) return;
for(int i=0; i<=29; ++i)
if(mn1[rt]>>i&1) num[rt][i]-=cnt[rt];
if(cnt[rt]&1) xsum[rt]^=mn1[rt];
mn1[rt]=val;
if(cnt[rt]&1) xsum[rt]^=mn1[rt];
for(int i=0; i<=29; ++i)
if(mn1[rt]>>i&1) num[rt][i]+=cnt[rt];
}
void pushDown(int rt)
{
add(ls,mn1[rt]);
add(rs,mn1[rt]);
}
void build(int rt,int L,int R)
{
if(L==R)
{
xsum[rt]=a[L];
for(int i=0; i<=29; ++i)
if(a[L]>>i&1) num[rt][i]=1;
cnt[rt]=1;
mn1[rt]=a[L];
mn2[rt]=(1<<30);
return;
}
int mid=(L+R)>>1;
build(ls,L,mid);
build(rs,mid+1,R);
pushUp(rt);
}
void update(int rt,int l,int r,int L,int R,int val)
{
if(mn1[rt]>=val) return;
if(l<=L&&R<=r&&val<mn2[rt])
{
add(rt,val);
return;
}
pushDown(rt);
int mid=(L+R)>>1;
if(l<=mid) update(ls,l,r,L,mid,val);
if(r>mid) update(rs,l,r,mid+1,R,val);
pushUp(rt);
}
int querySum(int rt,int l,int r,int L,int R)
{
if(l<=L&&R<=r) return xsum[rt];
pushDown(rt);
int mid=(L+R)>>1;
int ans=0;
if(l<=mid) ans^=querySum(ls,l,r,L,mid);
if(r>mid) ans^=querySum(rs,l,r,mid+1,R);
return ans;
}
int query(int rt,int l,int r,int L,int R,int p)
{
if(l<=L&&R<=r) return num[rt][p];
pushDown(rt);
int ans=0;
int mid=(L+R)>>1;
if(l<=mid) ans+=query(ls,l,r,L,mid,p);
if(r>mid) ans+=query(rs,l,r,mid+1,R,p);
return ans;
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1; i<=n; ++i) scanf("%d",&a[i]);
build(1,1,n);
while(q--)
{
int op,l,r,x;
scanf("%d%d%d%d",&op,&l,&r,&x);
if(op==1) update(1,l,r,1,n,x);
else
{
int res=querySum(1,l,r,1,n);
res^=x;
if(res==0) puts("0");
else
{
int p=log2(res);
printf("%d\n",(x>>p&1)+query(1,l,r,1,n,p));
}
}
}
return 0;
}