牛客小白月赛16选做

F F F 小石的妹子

题意:对于每个妹子有两个属性,小石进行多轮,每轮找出不被完全大于的那些人,赋值为 1 1 1,然后再从剩下的选取并赋值为 2 2 2,以此类推。
题解:二维偏序问题,但是我一开始逆序对数量即可判断,实际上不可。
我们把问题转换成,这次要选择的等级: R k j = m a x ( R k i ) + 1 Rk_j=max(Rk_i)+1 Rkj=max(Rki)+1,并且 R k i Rk_i Rki一定严格大于 R k j Rk_j Rkj。并且一定存在之前的一个比自己严格大,(但不是所有的比自己大。
这是关键,好好体会。
二维偏序可以用,排序一维,另一维进行权值线段树即可。

#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
typedef long long ll;
using namespace std;

const int maxn = 500050;

struct tree2{
	tree2 *lson,*rson;
	int x;
}dizhi[maxn<<1],*root=&dizhi[0];

int n,m,t=1,a[maxn];

void push_up(tree2 *tree){
    tree->x=max(tree->lson->x,tree->rson->x);
}

void build(tree2 *tree,int l,int r){
	if(l==r){
		tree->x=0;
		return ;
	}
	int mid=(l+r)>>1;
	tree->lson=&dizhi[t++];
	tree->rson=&dizhi[t++];
	build(tree->lson,l,mid);
	build(tree->rson,mid+1,r);
	push_up(tree);
}

void change(tree2 *tree,int l,int r,int x,int d){
	if(l==r){
        tree->x=d;
		return ;
	}
	int mid=(l+r)>>1;
	if(x<=mid)change(tree->lson,l,mid,x,d);
	else change(tree->rson,mid+1,r,x,d);
	push_up(tree);
}

int query(tree2 *tree,int l,int r,int x,int y){
	if(x<=l&&y>=r)return tree->x;
	int mid=(l+r)>>1;
	int t1=0,t2=0;
	if(x<=mid)t1=query(tree->lson,l,mid,x,y);
	if(y>mid)t2=query(tree->rson,mid+1,r,x,y);
	return max(t1,t2);
}

struct node{
    int x,y,index;
    friend bool operator < (node a,node b){
        if(a.x==b.x)return a.y>b.y;
        return a.x>b.x;
    }
}A[maxn];

int ans[maxn],lsh[maxn];

int main(){
    cin>>n;
    FOR(i,1,n){
        scanf("%d%d",&A[i].x,&A[i].y);
        A[i].index=i,lsh[i]=A[i].y;
    }
    sort(lsh+1,lsh+1+n);
    sort(A+1,A+1+n);
    FOR(i,1,n)A[i].y=lower_bound(lsh+1,lsh+1+n,A[i].y)-lsh;
    build(root,1,n);
    FOR(i,1,n){
        int cnt=query(root,1,n,A[i].y+1,n)+1;
        ans[A[i].index]=cnt;
        change(root,1,n,A[i].y,cnt);
    }
    FOR(i,1,n)printf("%d\n",ans[i]);
}

H H H 小阳的贝壳

题意:支持区间加和区间 g c d gcd gcd查询的数据结构(中间还有个相邻差最大值。
前两个要求很简单,我们可以利用差分实现,对于差分序列的最大值就是相邻差最大值,区间加也就是差分序列的线段树上的单点的修改。
第三个要求是区间 g c d gcd gcd的维护。
首先我们可以知道区间 g c d gcd gcd是可以合并的,那么如何支持带修的区间 g c d gcd gcd维护呢?
对于 g c d gcd gcd的一个性质:
g c d ( a , b ) = g c d ( a , b − a ) gcd(a,b)=gcd(a,b-a) gcd(a,b)=gcd(a,ba)
g c d ( a , b , c ) = g c d ( a , b − a , c − b ) = g c d ( a , g c d ( b − a , c − b ) ) gcd(a,b,c)=gcd(a,b-a,c-b)=gcd(a,gcd(b-a,c-b)) gcd(a,b,c)=gcd(a,ba,cb)=gcd(a,gcd(ba,cb))
也就是区间 g c d [ l , r ] = g c d ( a [ l ] , g c d ( a [ l + 1 ] − a [ l ] , . . . . , a [ r ] − a [ r − 1 ] ) ) gcd[l,r]=gcd(a[l],gcd(a[l+1]-a[l],....,a[r]-a[r-1])) gcd[l,r]=gcd(a[l],gcd(a[l+1]a[l],....,a[r]a[r1]))
也就是维护一个差分序列的 g c d gcd gcd即可,对差分序列的单点修改,只会影响单点 g c d gcd gcd,区间 g c d gcd gcd支持单点修改。

#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
#define inf 0x3f3f3f
typedef long long ll;
using namespace std;

const int maxn=5e5+10;
struct tree2{
	tree2 *lson,*rson;
	ll x;
	ll mx,mi,gcd;
}dizhi[maxn<<1],*root=&dizhi[0];

int n,m,t=1;
ll a[maxn],b[maxn];

void push_up(tree2 *tree){
	tree->x=tree->lson->x+tree->rson->x;
	tree->mx=max(tree->lson->mx,tree->rson->mx);
	tree->mi=min(tree->lson->mi,tree->rson->mi);
	tree->gcd=__gcd(tree->lson->gcd,tree->rson->gcd);
}

void build(tree2 *tree,int l,int r){
	if(l==r){
		tree->gcd=tree->mx=tree->mi=tree->x=a[l];
		return ;
	}
	int mid=(l+r)>>1;
	tree->lson=&dizhi[t++];
	tree->rson=&dizhi[t++];
	build(tree->lson,l,mid);
	build(tree->rson,mid+1,r);
	push_up(tree);
}

void change(tree2 *tree,int l,int r,int x,int d){
	if(l==r){
		tree->x+=d;
		tree->mx+=d;
		tree->mi+=d;
		tree->gcd+=d;
		return ;
	}
	int mid=(l+r)>>1;
	if(x<=mid)change(tree->lson,l,mid,x,d);
	else change(tree->rson,mid+1,r,x,d);
	push_up(tree);
}

ll querysum(tree2 *tree,int l,int r,int x,int y){
	if(x<=l&&y>=r)return tree->x;
	int mid=(l+r)>>1;
	ll t1=0,t2=0;
	if(x<=mid)t1=querysum(tree->lson,l,mid,x,y);
	if(y>mid)t2=querysum(tree->rson,mid+1,r,x,y);
	return t1+t2;
}

ll querymax(tree2 *tree,int l,int r,int x,int y){
	if(x<=l&&y>=r)return tree->mx;
	int mid=(l+r)>>1;
	ll t1=-inf,t2=-inf;
	if(x<=mid)t1=querymax(tree->lson,l,mid,x,y);
	if(y>mid)t2=querymax(tree->rson,mid+1,r,x,y);
	return max(t1,t2);
}

ll querymin(tree2 *tree,int l,int r,int x,int y){
	if(x<=l&&y>=r)return tree->mi;
	int mid=(l+r)>>1;
	ll t1=inf,t2=inf;
	if(x<=mid)t1=querymin(tree->lson,l,mid,x,y);
	if(y>mid)t2=querymin(tree->rson,mid+1,r,x,y);
	return min(t1,t2);
}

ll querygcd(tree2 *tree,int l,int r,int x,int y){
	if(x<=l&&y>=r)return tree->gcd;
	int mid=(l+r)>>1;
	ll t=0;
	if(x<=mid)t=__gcd(t,querygcd(tree->lson,l,mid,x,y));
	if(y>mid)t=__gcd(t,querygcd(tree->rson,mid+1,r,x,y));
	return abs(t);
}

int main(){
    cin>>n>>m;
    FOR(i,1,n)scanf("%lld",&b[i]);
    FOR(i,1,n)a[i]=b[i]-b[i-1];
    build(root,1,n);
    FOR(M,1,m){
        int opt,L,R;scanf("%d%d%d",&opt,&L,&R);
        if(opt==1){
            ll d;scanf("%lld",&d);
            change(root,1,n,L,d);
            if(R+1<=n)change(root,1,n,R+1,-d);
        }
        if(opt==2){
            if(L==R){puts("0");continue;}
            ll tmp1=-querymin(root,1,n,L+1,R);
            ll tmp2=querymax(root,1,n,L+1,R);
            printf("%lld\n",max(tmp1,tmp2));
        }
        if(opt==3){
            ll now=querysum(root,1,n,1,L);
            if(L==R){printf("%lld\n",now);continue;}
            printf("%lld\n",__gcd(now,abs(querygcd(root,1,n,L+1,R))));
        }
    }
}

J J J 小雨坐地铁

题意:有 m m m条地铁线,每条地铁线从一个站到另一个站有自己的花费,上车也有独立的花费。问从某个车站到另一个车站的最少花费。地铁线总共只有 500 500 500,车站总共只有 1000 1000 1000
题解:对于 m m m条地铁线,即在图上建立 m m m条地铁线,对于每个车站,在每条地铁线上都会建立镜像的一个点方便处理,还有一个原来的车站店,镜像点到原来的点没有花费,反之有上车费。
对于同一地铁线上的点自然是另一个花费直接建双向边即可。
这种建图类似于现实场景中的了十分巧妙。
题解:

#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<b;i++)
#define inf 0x3f3f3f3f3f3f3f3f
typedef long long ll;
using namespace std;

const int maxm = 2e6+100;
const int maxn = 1e6+500;

struct Edge{
    int from,to;ll dist;
    Edge(){}
    Edge(int _from,int _to,ll _dist):from(_from),to(_to),dist(_dist){}
    friend bool operator < (Edge a,Edge b){
        return a.dist<b.dist;
    }
};

int n,m;
Edge ed[maxm];int he[maxn],ne[maxn],etop=1;
priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > >q;
ll d[maxn];bool vis[maxn];int st[maxn];

void insert(int u,int v,ll w){
    ed[etop]=Edge(u,v,w);
    ne[etop]=he[u];
    he[u]=etop++;
}

ll dijsktra(int s,int t){
    memset(d,0x3f,sizeof(d));
    memset(vis,0,sizeof(vis));
    q.push(make_pair(d[s]=0,s));
    while(!q.empty()){
        int u=q.top().second;q.pop();
        if(vis[u])continue;
        vis[u]=true;
        for(int i=he[u];i;i=ne[i]){
            Edge& e=ed[i];
            if(d[e.to]>d[u]+e.dist){
                d[e.to]=d[u]+e.dist;
                q.push(make_pair(d[e.to],e.to));
            }
        }
    }
    return d[t]==inf?-1:d[t];
}

int get(int i,int j){return i*n+j;}

int main(){
    int s,t,a,b,p,x,tmp;
    cin>>n>>m>>s>>t;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&a,&b,&p);
        for(int j=1;j<=p;j++){
            scanf("%d",&st[j]);
            insert(get(i,st[j]),st[j],0);
            insert(st[j],get(i,st[j]),a);
        }
        for(int j=1;j<p;j++){
            insert(get(i,st[j]),get(i,st[j+1]),b);
        }
        for(int j=2;j<=p;j++){
            insert(get(i,st[j]),get(i,st[j-1]),b);
        }
    }
    cout<<dijsktra(s,t)<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值