一、队列
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<cctype>
#include<cmath>
#include<map>
#include<vector>
#define For(i,x,y) for(int i=x;i<=y;++i)
#define Fov(i,x,y) for(int i=x;i>=y;--i)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int n_max=1e6+5;
int q1[n_max],q2[n_max],a[n_max],dp1[n_max],dp2[n_max],k,n;
int main()
{
scanf("%d%d",&n,&k);
For(i,0,n-1)
scanf("%d",&a[i]);
int s1=0,s2=0,t1=0,t2=0;
For(i,0,n-1)
{
while(s1<t1&&a[q1[t1-1]]>=a[i]) t1--;
while(s2<t2&&a[q2[t2-1]]<=a[i]) t2--;
q1[t1++]=i;
q2[t2++]=i;
if(i-k+1>=0)
{
dp1[i-k+1]=a[q1[s1]];
dp2[i-k+1]=a[q2[s2]];
if(q1[s1]==i-k+1) s1++;
if(q2[s2]==i-k+1) s2++;
}
}
For(i,0,n-k)
printf("%d%c",dp1[i],i==n-k?'\n':' ');
For(i,0,n-k)
printf("%d%c",dp2[i],i==n-k?'\n':' ');
return 0;
}
维护两个队列q1(求最小值),q2(求最大值) ;每次往队列中插入当前数字下标时,判断该数字与队尾元素大小关系;对于q1,如果队尾元素比当前数字大,那队尾元素不可能会是这k个数字中的最小值,移除队尾元素;同理对于q2,如果队尾元素比当前数字小,那么队尾元素不可能会是这k个数字中的最大值,移除队尾元素;不断执行此操作直至队尾元素不满足上述条件或者为空,此时就可以插入当前元素;而当队首元素不属于目前求的这k个数的序号范围时,应该移除队首元素。
题目2:HDU3415--- Max Sum of Max-K-sub-sequence
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<cctype>
#include<cmath>
#include<map>
#include<vector>
#define For(i,x,y) for(int i=x;i<=y;++i)
#define Fov(i,x,y) for(int i=x;i>=y;--i)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int n_max=1e5+5;
int a[2*n_max],dp[n_max],q[2*n_max],sum[2*n_max];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,k;
scanf("%d%d",&n,&k);
For(i,0,n-1)
{
scanf("%d",&a[i]);
sum[i+1]=sum[i]+a[i];
}
For(i,0,k-1)
{
a[n+i]=a[i];
sum[n+i+1]=sum[n+i]+a[i];
}
int s=0,t=1,ans=-inf,l,r;
q[0]=0;
For(i,1,n+k)
{
if(sum[i]-sum[q[s]]>ans)
{
ans=sum[i]-sum[q[s]];
l=q[s]+1;
r=i;
}
while(s<t&&sum[q[t-1]]>=sum[i]) t--;
q[t++]=i;
if(i-k>=0&&q[s]==i-k) s++;
}
printf("%d %d %d\n",ans,l,r>n?r-n:r);
}
return 0;
}
求的是环状序列,可以往序列尾部补加k-1个元素,数值等于序列前k-1个数;求出sum[i],前i个数的前缀和;然后维护队列q,队列维护的是sum的序号;对于每个sum[i],如果能够得到i之前的k个前缀和的最小值x,就可以得到i-k+1~i这k个数的最大子串和为sum[i]-x。
二、栈
class Solution {
public:
bool find132pattern(vector<int>& nums) {
int x=-0x3f3f3f3f;
stack<int>s;
for(int i=nums.size()-1;i>=0;--i)
{
if(nums[i]<x) return true;
while(!s.empty()&&nums[i]>s.top())
{
x=s.top();
s.pop();
}
s.push(nums[i]);
}
return false;
}
};
三、二叉索引树(树状数组)
对于某只牛以它的听力值发声只能与其他听力值比它小的牛交流,因而可以按牛的听力值从小到大排列,维护两个树状数组cnt和dis。
处理牛i时,可以保证数组存储着所有听力值比它小的牛的数据;i的坐标为x,对于某只牛j(坐标为y),那么发声值应该为hear[i]*|x-y|,很明显要判断x和y的相对大小,因而可以查询坐标x之前以及之后牛的数量(t1,t2)、坐标和(d1,d2),结果为hear[i]*(t1*x-d1+d2-t2*x)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<cctype>
#include<cmath>
#include<map>
#include<vector>
#define For(i,x,y) for(int i=x;i<=y;++i)
#define Fov(i,x,y) for(int i=x;i>=y;--i)
using namespace std;
typedef long long ll;
inline int read()
{
char ch = getchar(); int x = 0, f = 1;
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while('0' <= ch && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
const int inf=0x3f3f3f3f;
const int n_max=2e5+10;
ll cnt[n_max],dis[n_max];
pair<ll,ll>cow[n_max];
ll sum(ll* bit,int i)
{
ll s=0;
while(i)
{
s+=bit[i];
i-=i&-i;
}
return s;
}
ll sum(ll* bit,int l,int r)
{
return sum(bit,r-1)-sum(bit,l-1);
}
void add(ll* bit,int i,ll x)
{
while(i<n_max)
{
bit[i]+=x;
i+=i&-i;
}
}
int main()
{
int n;
scanf("%d",&n);
For(i,0,n-1)
scanf("%I64d%I64d",&cow[i].first,&cow[i].second);
sort(cow,cow+n);
ll ans=0;
For(i,0,n-1)
{
ll y=cow[i].first,x=cow[i].second;
ll l=sum(cnt,1,x),r=sum(cnt,x+1,n_max);
ans+=y*(l*x-sum(dis,1,x)+sum(dis,x+1,n_max)-r*x);
add(cnt,x,1);
add(dis,x,x);
}
printf("%I64d\n",ans);
return 0;
}
题目2:POJ3067---Japan
对于两条直线(w1,e1)、(w2,e2),只有当(w1-w2)*(e1-e2)<0时才相交
可以将西坐标w从小到大排列(w相等时e小的排前面,否则下面处理中认为上述条件=0时也成立),处理路i时,可以保证树状数组存储着所有西坐标比它小的路数据,此时查询之前的东坐标比它大的路的数量即可
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<cctype>
#include<cmath>
#include<map>
#include<vector>
#define For(i,x,y) for(int i=x;i<=y;++i)
#define Fov(i,x,y) for(int i=x;i>=y;--i)
using namespace std;
typedef long long ll;
inline int read()
{
char ch = getchar(); int x = 0, f = 1;
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while('0' <= ch && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
const int inf=0x3f3f3f3f;
const int n_max=1e6+10;
ll bit[n_max];
int n,m,k;
struct node
{
int west,east;
}road[n_max];
ll sum(int i)
{
ll res=0;
while(i)
{
res+=bit[i];
i-=i&-i;
}
return res;
}
void add(int i,ll x)
{
while(i<=m)
{
bit[i]+=x;
i+=i&-i;
}
}
bool compare(const node& a,const node& b)
{
if(a.west==b.west) return a.east<b.east;
return a.west<b.west;
}
int main()
{
int T,cas=0;
scanf("%d",&T);
while(T--)
{
memset(bit,0,sizeof(bit));
scanf("%d%d%d",&n,&m,&k);
For(i,0,k-1)
scanf("%d%d",&road[i].west,&road[i].east);
sort(road,road+k,compare);
ll ans=0;
For(i,0,k-1)
{
ans+=i-sum(road[i].east);
add(road[i].east,1);
}
printf("Test case %d: %I64d\n",++cas,ans);
}
return 0;
}
如果当前处于村庄(x1,y1),那么YJJ能够获得利润的下一个村庄(x2,y2)必定有x1<x2且y1<y2;
x、y的范围很大,直接处理必定超时,而村庄的数量较少,因而可以利用村庄坐标离散化处理数据。
将村庄按y坐标从小到大排列(y相等则x大的排前面,否则下面处理时认为y1==y2、x1<x2也成立),离散化处理x坐标(从小到大排列),处理村庄i时,保证树状数组存储着所有y坐标比它小的村庄数据,此时查询之前x坐标排名比它靠前的村庄即可(由于不能保证由当前村庄出发可以到达任意村庄,因而之前所有的村庄都可能出现最大值max,取max+当前村庄利润)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<cctype>
#include<cmath>
#include<map>
#include<vector>
#include<utility>
#define For(i,x,y) for(int i=x;i<=y;++i)
#define Fov(i,x,y) for(int i=x;i>=y;--i)
#define midf(a,b) ((a)+(b)>>1)
#define Num1(_) (_)<<1
#define Num2(_) ((_)<<1)|1
#define lowbit(x) x&-x
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
inline int read()
{
char ch=getchar(); int x=0, f=1;
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
const int inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const int n_max=1e5+10;
int bit[n_max],n,x[n_max],cnt;
struct node
{
int x,y,v;
bool operator<(const node& a)const
{
return y==a.y?x>a.x:y<a.y;
}
}vil[n_max];
int sum(int i)
{
int res=0;
while(i)
{
res=max(res,bit[i]);
i-=i&-i;
}
return res;
}
void add(int i,int x)
{
while(i<=cnt)
{
bit[i]=max(bit[i],x);
i+=i&-i;
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
vector<int>X;
For(i,1,n)
{
scanf("%d%d%d",&vil[i].x,&vil[i].y,&vil[i].v);
X.push_back(vil[i].x);
bit[i]=0;
}
sort(vil+1,vil+n+1);
sort(X.begin(),X.end());
X.erase(unique(X.begin(),X.end()),X.end());
cnt=X.size();
int ans=0;
For(i,1,n)
{
int k=lower_bound(X.begin(),X.end(),vil[i].x)-X.begin();
int temp=vil[i].v+sum(k);
ans=max(ans,temp);
add(k+1,temp);
}
printf("%d\n",ans);
}
return 0;
}
题目4:ACM-ICPC 2018 徐州赛区网络预赛H---Ryuji doesn't want to study
由题意得对于某一次询问(l,r), ,可式子可化为
,而且本题为单点修改,可以使用两个树状数组bit,sum分别维护(n-i+1)*a[i]和a[i]的值.
#include <bits/stdc++.h>
#define For(i,x,y) for(int i=(x);i<=(y);++i)
#define Fov(i,x,y) for(int i=(x);i>=(y);--i)
#define Fo(i,x,y) for(int i=(x);i<(y);++i)
#define midf(a,b) ((a)+(b)>>1)
#define L p<<1
#define R (p<<1)|1
#define ss(_) scanf("%s",_)
#define si(_) scanf("%d",&_)
#define sii(x,y) scanf("%d%d",&x,&y)
#define siii(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define mem(x,y) memset(x,y,sizeof(x))
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
inline int read()
{
char ch=getchar(); int x=0, f=1;
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
const int inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const int n_max=1e5+10;
ll a[n_max],bit[n_max],sum[n_max];
int n,q;
void add(int i,ll x,ll* b)
{
while(i<=n)
{
b[i]+=x;
i+=i&-i;
}
}
ll query(int i,ll* b)
{
ll ans=0;
while(i)
{
ans+=b[i];
i-=i&-i;
}
return ans;
}
int main()
{
int x,y,op;
sii(n,q);
For(i,1,n)
{
scanf("%lld",&a[i]);
add(i,a[i],sum);
add(i,a[i]*(n-i+1),bit);
}
For(i,1,q)
{
siii(op,x,y);
if(op==1)
{
ll t1=query(y,bit)-query(x-1,bit);
ll t2=query(y,sum)-query(x-1,sum);
printf("%lld\n",t1-t2*(n-y));
}
else
{
add(x,y-a[x],sum);
add(x,(n-x+1)*(y-a[x]),bit);
a[x]=y;
}
}
return 0;
}
三、并查集
题目1:POJ1182---食物链
假设权值f[i]为i与根节点的关系,0为同类,1为吃,2为被吃
int find(int x)
{
if(p[x]==x)
return x;
int t=p[x];
p[x]=find(p[x]);
f[x]=(f[x]+f[t])%3;
return p[x];
}
在溯源的过程中进行路径压缩p[x]=find(p[x]);
根据向量加法可得A->C=A->B+B->C=z=(x+y)%3(可能加起来超过2,因而要对3取余),可解决压缩过程中各节点与根节点的关系
void unite(int x,int y,int a,int b,int c)
{
if(r[x]>r[y])
{
p[y]=x;
f[y]=(f[a]-f[b]-c+3)%3;
}
else
{
p[x]=y;
f[x]=(f[b]+c-f[a]+3)%3;
if(r[x]==r[y])
++r[y];
}
}
r[i]表示以i为根节点的树的高度,合并过程中应将高度小的树往大的树合并,合成后高度=较高的树的高度,若两树高度相等,合成后高度加1
根据向量关系可得以不同方向合成时的关系式B->D=B->A+A->C+C->D=z=(-x+k+y+3)%3(进行加3补偿,防止出现负数)D->B=D->C+C->A+A->B=z=(-y-k+x+3)%3
最后得出结果(注意进行初始化)
void init()
{
for(int i=1;i<=n;++i)
p[i]=i;
}
int main()
{
scanf("%d%d",&n,&q);
init();
int a,b,c,ans=0;
for(int i=0;i<q;++i)
{
scanf("%d%d%d",&c,&a,&b);
if(a>n||b>n)
{
++ans;
continue;
}
--c;
int x=find(a),y=find(b);
if(x==y&&c!=(f[a]-f[b]+3)%3)
++ans;
else if(x!=y)
unite(x,y,a,b,c);
}
printf("%d\n",ans);
return 0;
}
题目2:HDU3038---How Many Answers Are Wrong
定义权值dis[i]为i到根节点之间所有数之和,也可以看做两点间距离
往根节点大的方向合成
可以看出对于x=p[a]>y=p[b],dis[y][x]=dis[a]-dis[b]-c,对于x<=y,dis[x][y]=c-dis[a]+dis[b]
int find(int x)
{
if(p[x]==x)
return x;
int temp=p[x];
p[x]=find(p[x]);
dis[x]+=dis[temp];
return p[x];
}
void unite(int a,int b,int x,int y,int d)
{
if(x>y)
{
p[y]=x;
dis[y]=dis[a]-dis[b]-d;
}
else
{
p[x]=y;
dis[x]=dis[b]+d-dis[a];
}
}
最后得出结果(注意进行初始化,还有要进行--a操作,初始化也要从0开始,否则无法处理a=b的情况)
void init()
{
for(int i=0; i<=n; ++i)
p[i]=i;
memset(dis,0,sizeof(dis));
}
while(~scanf("%d%d",&n,&q))
{
int ans=0;
init();
for(int i=0; i<q; ++i)
{
scanf("%d%d%d",&a,&b,&d);
--a;
int x=find(a),y=find(b);
if(x==y&&dis[a]!=dis[b]+d)
++ans;
else if(x!=y)
unite(a,b,x,y,d);
}
printf("%d\n",ans);
}
四、线段树
五、主席树
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<cctype>
#include<cmath>
#include<map>
#include<vector>
#define For(i,x,y) for(int i=x;i<=y;++i)
#define Fov(i,x,y) for(int i=x;i>=y;--i)
#define midf(a,b) ((a)+(b)>>1)
#define Num1(_) (_)<<1
#define Num2(_) ((_)<<1)|1
using namespace std;
typedef long long ll;
inline int read()
{
char ch=getchar(); int x=0, f=1;
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
const int inf=0x3f3f3f3f;
const int n_max=1e5+10;
struct node
{
int l,r,sum;
}tree[n_max*20];
int tot,rt[n_max],a[n_max];
vector<int>v;
int getrank(int x)
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void build(int l,int r,int& x)
{
x=++tot;
tree[x].sum=0;
if(l==r) return;
int mid=midf(l,r);
build(l,mid,tree[x].l);
build(mid+1,r,tree[x].r);
}
void update(int l,int r,int& x,int y,int k)
{
x=++tot;tree[x]=tree[y];tree[x].sum++;
if(l==r) return;
int mid=midf(l,r);
if(k<=mid) update(l,mid,tree[x].l,tree[y].l,k);
else update(mid+1,r,tree[x].r,tree[y].r,k);
}
int query(int l,int r,int x,int y,int k)
{
if(l==r) return l;
int mid=midf(l,r),sum=tree[tree[y].l].sum-tree[tree[x].l].sum;
if(k<=sum) return query(l,mid,tree[x].l,tree[y].l,k);
else return query(mid+1,r,tree[x].r,tree[y].r,k-sum);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
v.clear();
int n,m;
scanf("%d%d",&n,&m);
For(i,1,n)
{
scanf("%d",&a[i]);
v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
int cnt=v.size();
tot=0;build(1,cnt,rt[0]);
For(i,1,n)
update(1,cnt,rt[i],rt[i-1],getrank(a[i]));
int l,r,k;
For(i,1,m)
{
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",v[query(1,cnt,rt[l-1],rt[r],k)-1]);
}
}
return 0;
}
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<cctype>
#include<cmath>
#include<map>
#include<vector>
#define For(i,x,y) for(int i=x;i<=y;++i)
#define Fov(i,x,y) for(int i=x;i>=y;--i)
#define midf(a,b) ((a)+(b)>>1)
#define Num1(_) (_)<<1
#define Num2(_) ((_)<<1)|1
using namespace std;
typedef long long ll;
inline int read()
{
char ch=getchar(); int x=0, f=1;
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
const int inf=0x3f3f3f3f;
const int n_max=2e5+10;
struct node
{
int l,r,par,deep;
}tree[n_max*50];
int cnt,n,m,rt[n_max];
void build(int l,int r,int& x)
{
x=++cnt;
if(l==r)
{
tree[x].par=l;
tree[x].deep=1;
return;
}
int mid=midf(l,r);
build(l,mid,tree[x].l);
build(mid+1,r,tree[x].r);
}
void update(int l,int r,int& x,int y,int fa,int fb)
{
x=++cnt;
tree[x]=tree[y];
if(l==r)
{
tree[x].par=fb;
return;
}
int mid=midf(l,r);
if(fa<=mid) update(l,mid,tree[x].l,tree[y].l,fa,fb);
else update(mid+1,r,tree[x].r,tree[y].r,fa,fb);
}
int query(int l,int r,int x,int k)
{
if(l==r) return x;
int mid=midf(l,r);
if(k<=mid) return query(l,mid,tree[x].l,k);
else return query(mid+1,r,tree[x].r,k);
}
int Find(int x,int k)
{
int p=query(1,n,x,k);
if(k==tree[p].par)
return p;
return Find(x,tree[p].par);
}
void add(int l,int r,int x,int k)
{
if(l==r)
{
tree[x].deep++;
return;
}
int mid=midf(l,r);
if(k<=mid) add(l,mid,tree[x].l,k);
else add(mid+1,r,tree[x].r,k);
}
int main()
{
n=read(),m=read();
int a,b,f,k,ans=0;
build(1,n,rt[0]);
For(i,1,m)
{
f=read();
if(f==1)
{
a=read()^ans,b=read()^ans;
rt[i]=rt[i-1];
int fa=Find(rt[i],a),fb=Find(rt[i],b);
if(tree[fa].par!=tree[fb].par)
{
if(tree[fa].deep>tree[fb].deep)
swap(fa,fb);
update(1,n,rt[i],rt[i-1],tree[fa].par,tree[fb].par);
if(tree[fa].deep==tree[fb].deep)
add(1,n,rt[i],tree[fb].par);
}
}
else if(f==2)
{
k=read()^ans;
rt[i]=rt[k];
}
else
{
rt[i]=rt[i-1];
a=read()^ans,b=read()^ans;
int fa=Find(rt[i],a),fb=Find(rt[i],b);
if(tree[fa].par==tree[fb].par)
ans=1;
else
ans=0;
printf("%d\n",ans);
}
}
return 0;
}
六、LCA算法
RMQ问题:ST算法
将序列中数值相同的数划为一段,用数组id[i]保存数字i所属段序号,并用left[i]和right[i]保存数字i所属段的左端点和右端点数字序号。
对于每一次询问,如果左端点l所在段序号等于右端点r段序号,很明显答案为r-l+1;否则此询问包括两段以上,特殊处理头尾两段,对于中间段采用ST表快速搜索。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
#include <string>
#include <cctype>
#include <cmath>
#include <map>
#include <vector>
#include <utility>
#define For(i,x,y) for(int i=(x);i<=(y);++i)
#define Fov(i,x,y) for(int i=(x);i>=(y);--i)
#define Fo(i,x,y) for(int i=(x);i<(y);++i)
#define midf(a,b) ((a)+(b)>>1)
#define L p<<1
#define R (p<<1)|1
#define ss(_) scanf("%s",_)
#define si(_) scanf("%d",&_)
#define sii(x,y) scanf("%d%d",&x,&y)
#define siii(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define mem(x,y) memset(x,y,sizeof(x))
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
inline int read()
{
char ch=getchar(); int x=0, f=1;
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
const int inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const int n_max=1e5+10;
int Left[n_max],Right[n_max],id[n_max],a[n_max],st[n_max][20];
void init(int n)
{
for(int j=1,k=1<<j;k<=n;++j,k=1<<j)
for(int i=1;i+k-1<=n;++i)
st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
int rmq(int l,int r)
{
int k=log(double(r-l+1))/log(2.0);
return max(st[l][k],st[r-(1<<k)+1][k]);
}
int main()
{
//freopen("in.txt","r",stdin);
int n,q;
while(~si(n)&&n)
{
si(q);
For(i,1,n) si(a[i]);
int k=1,tot=0;
while(k<=n)
{
int t=upper_bound(a+k,a+n+1,a[k])-a-k,x=k+t-1;
st[++tot][0]=t;
For(j,k,x)
{
Left[j]=k;
Right[j]=x;
id[j]=tot;
}
k=x+1;
}
init(tot);
int l,r;
For(i,1,q)
{
sii(l,r);
int ans;
if(id[l]==id[r]) ans=r-l+1;
else ans=max(Right[l]-l+1,r-Left[r]+1);
if(id[l]+1<id[r]) ans=max(ans,rmq(id[l]+1,id[r]-1));
printf("%d\n",ans);
}
}
return 0;
}
①在线算法:ST
dis[i]:节点i与根节点距离;val[i]:节点i与最近红祖先节点距离
将每次询问的k个节点按val值从大到小排列,逐个处理每个节点;
此时有两种选择,一是直接将第一个节点(val值最大)变红,此时结果为第二个节点的val值;二是取两个节点的LCA变红(因为必须同时降低两个val值,否则没有意义),而且接下来每次都取之前节点的LCA变红,最小值可能会出现在某一次操作后
#include <bits/stdc++.h>
#define For(i,x,y) for(int i=(x);i<=(y);++i)
#define Fov(i,x,y) for(int i=(x);i>=(y);--i)
#define Fo(i,x,y) for(int i=(x);i<(y);++i)
#define midf(a,b) ((a)+(b)>>1)
#define L(_) (_)<<1
#define R(_) ((_)<<1)|1
#define fi first
#define se second
#define ss(_) scanf("%s",_)
#define si(_) scanf("%d",&_)
#define sii(x,y) scanf("%d%d",&x,&y)
#define siii(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define mem(x,y) memset(x,y,sizeof(x))
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
inline int read()
{
char ch=getchar(); int x=0, f=1;
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
const int inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const int n_max=2e5+10;
bool red[n_max];
ll dis[n_max],val[n_max];
int head[n_max],deep[n_max],p[n_max],fa[n_max][20],num[n_max],cnt;
struct node
{
int to,nex,w;
}e[2*n_max];
void add(int u,int v,int w)
{
e[++cnt].to=v;
e[cnt].nex=head[u];
e[cnt].w=w;
head[u]=cnt;
}
void dfs(int cur,int last)
{
if(red[cur]) p[cur]=cur;
else p[cur]=p[last];
val[cur]=dis[cur]-dis[p[cur]];
for(int i=head[cur];i;i=e[i].nex)
{
int v=e[i].to;
if(v==last) continue;
dis[v]=dis[cur]+e[i].w;
deep[v]=deep[cur]+1;
fa[v][0]=cur;
dfs(v,cur);
}
}
void init(int n)
{
for(int j=1;(1<<j)<=n;++j)
for(int i=1;i<=n;++i) fa[i][j]=fa[fa[i][j-1]][j-1];
}
int lca(int a,int b)
{
if(deep[a]<deep[b]) swap(a,b);
int k=0;
while((1<<k+1)<=deep[a]) ++k;
Fov(i,k,0) if(deep[a]-(1<<i)>=deep[b]) a=fa[a][i];
if(a==b) return a;
Fov(i,k,0) if(fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i];
return fa[a][0];
}
bool cmp(int a,int b)
{
return val[a]>val[b];
}
int main()
{
//freopen("in.txt","r",stdin);
int T; si(T);
while(T--)
{
int n,m,q,k,u,v,w; siii(n,m,q);
mem(red,0);
mem(head,0);
For(i,1,m)
{
si(u);
red[u]=true;
}
Fo(i,1,n)
{
siii(u,v,w);
add(u,v,w);
add(v,u,w);
}
dis[1]=val[n+1]=0;
dfs(1,0);
init(n);
For(i,1,q)
{
si(k);
Fo(j,0,k) si(num[j]);
num[k]=n+1;
sort(num,num+k,cmp);
ll ans=val[num[1]],lson=0,rson;
int cur,pre=num[0];
Fo(j,1,k)
{
cur=lca(pre,num[j]);
lson+=dis[pre]-dis[cur];
rson=min(val[num[j]],dis[num[j]]-dis[cur]);
lson=max(lson,rson);
pre=cur;
ans=min(ans,max(lson,val[num[j+1]]));
}
printf("%lld\n",ans);
}
}
return 0;
}
七、树链剖分
题目:ACM-ICPC 2018 焦作赛区网络预赛E---Jiu Yuan Wants to Eat
#include <bits/stdc++.h>
#define For(i,x,y) for(int i=(x);i<=(y);++i)
#define Fov(i,x,y) for(int i=(x);i>=(y);--i)
#define Fo(i,x,y) for(int i=(x);i<(y);++i)
#define midf(a,b) ((a)+(b)>>1)
#define L(_) (_)<<1
#define R(_) ((_)<<1)|1
#define fi first
#define se second
#define ss(_) scanf("%s",_)
#define si(_) scanf("%d",&_)
#define sii(x,y) scanf("%d%d",&x,&y)
#define siii(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define sl(_) scanf("%lld",&_)
#define mem(x,y) memset(x,y,sizeof(x))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>P;
inline int read()
{
char ch=getchar(); int x=0, f=1;
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
const int inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const int n_max=1e5+10;
int top[n_max],id[n_max],siz[n_max],par[n_max],son[n_max],depth[n_max],head[n_max];
int cnt,tot,n;
struct node
{
int to,nex;
}e[n_max<<1];
void add(int u,int v)
{
e[cnt].nex=head[u];
e[cnt].to=v;
head[u]=cnt++;
}
struct point
{
ull sum,add,mul;
}tree[n_max<<2];
void dfs1(int u ,int fa,int dep)
{
depth[u]=dep;
par[u]=fa;
siz[u]=1;
for(int i=head[u];~i;i=e[i].nex)
{
int v=e[i].to;
dfs1(v,u,dep+1);
siz[u]+=siz[v];
if(son[u]==-1||siz[son[u]]<siz[v]) son[u]=v;
}
}
void dfs2(int u,int t)
{
top[u]=t;
id[u]=++tot;
if(son[u]==-1) return;
dfs2(son[u],t);
for(int i=head[u];~i;i=e[i].nex)
{
int v=e[i].to;
if(v!=son[u]) dfs2(v,v);
}
}
void init()
{
mem(son,-1);
mem(head,-1);
cnt=tot=0;
}
void pushup(int p)
{
tree[p].sum=tree[L(p)].sum+tree[R(p)].sum;
}
void pushdown(int p,int len)
{
tree[L(p)].add=tree[L(p)].add*tree[p].mul+tree[p].add;
tree[R(p)].add=tree[R(p)].add*tree[p].mul+tree[p].add;
tree[L(p)].mul*=tree[p].mul;
tree[R(p)].mul*=tree[p].mul;
tree[L(p)].sum=tree[L(p)].sum*tree[p].mul+tree[p].add*(len-(len>>1));
tree[R(p)].sum=tree[R(p)].sum*tree[p].mul+tree[p].add*(len>>1);
tree[p].add=0; tree[p].mul=1;
}
void build(int p,int l,int r)
{
tree[p].add=0; tree[p].mul=1;
if(l==r)
{
tree[p].sum=0;
return;
}
int mid=midf(l,r);
build(L(p),l,mid);
build(R(p),mid+1,r);
pushup(p);
}
void update(int p,int l,int r,int x,int y,int v,ull w)
{
if(x<=l&&y>=r)
{
if(v==1)
{
tree[p].sum*=w;
tree[p].mul*=w;
tree[p].add*=w;
}
else
{
tree[p].sum+=(r-l+1)*w;
tree[p].add+=w;
}
return;
}
pushdown(p,r-l+1);
int mid=midf(l,r);
if(x<=mid) update(L(p),l,mid,x,y,v,w);
if(y>mid) update(R(p),mid+1,r,x,y,v,w);
pushup(p);
}
ull query(int p,int l,int r,int x,int y)
{
if(x<=l&&y>=r) return tree[p].sum;
pushdown(p,r-l+1);
int mid=midf(l,r);
ull res=0;
if(x<=mid) res+=query(L(p),l,mid,x,y);
if(y>mid) res+=query(R(p),mid+1,r,x,y);
return res;
}
void cover(int x,int y,int v,ull w)
{
while(top[x]!=top[y])
{
if(depth[top[x]]<depth[top[y]]) swap(x,y);
update(1,1,n,id[top[x]],id[x],v,w);
x=par[top[x]];
}
if(depth[x]<depth[y]) swap(x,y);
update(1,1,n,id[y],id[x],v,w);
}
ull ask(int x,int y)
{
ull res=0;
while(top[x]!=top[y])
{
if(depth[top[x]]<depth[top[y]]) swap(x,y);
res+=query(1,1,n,id[top[x]],id[x]);
x=par[top[x]];
}
if(depth[x]<depth[y]) swap(x,y);
res+=query(1,1,n,id[y],id[x]);
return res;
}
int main()
{
//freopen("in.txt","r",stdin);
while(~si(n))
{
int u,v,q,x,y;
ull w;
init();
build(1,1,n);
For(i,2,n)
{
si(u);
add(u,i);
}
dfs1(1,-1,1);
dfs2(1,1);
si(q);
For(i,1,q)
{
si(v);
if(v==4)
{
sii(x,y);
printf("%llu\n",ask(x,y));
}
else if(v==3)
{
sii(x,y);
cover(x,y,2,1);
cover(x,y,1,-1);
}
else
{
scanf("%d%d%llu",&x,&y,&w);
cover(x,y,v,w);
}
}
}
return 0;
}