数据结构

一、队列

题目1:POJ2823---Sliding Window

#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。

二、栈

题目:leetcode456---132模式

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;
    }
};

三、二叉索引树(树状数组)

题目1:POJ1990---MooFest

对于某只牛以它的听力值发声只能与其他听力值比它小的牛交流,因而可以按牛的听力值从小到大排列,维护两个树状数组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;
}

题目3:HDU6447---YJJ's Salesman

如果当前处于村庄(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);
    }

四、线段树

 

五、主席树

题目1:HDU2665---Kth number 

#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;
}

题目2:BZOJ3674---可持久化并查集加强版

#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算法

题目:POJ3368---Frequent values

将序列中数值相同的数划为一段,用数组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

题目:ZOJ4048---Red Black Tree

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值