2022/3/13

142 篇文章 1 订阅
142 篇文章 0 订阅

p3919 主席树模板

这个人讲的挺好P3919 【模板】可持久化数组 -初步探究主席树 - hyfhaha 的博客 - 洛谷博客 (luogu.com.cn)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int inf=0x3f3f3f3f;
int top,n,m,a[1000006],p,op,x,y,root[1000006*35];//root存的是每个版本的根节点编号
struct Node{
    int l,r,val;
}tree[1000006*35];//存的是每个版本的树
int clone(int node){
    top++;
    tree[top]=tree[node];//复制一个新的树
    return top;
}
int maketree(int node,int l,int r){//开始建树
    node=++top;
    if(l==r){
        tree[node].val=a[l];
        return top;
    }
    int mid=l+r>>1;
    tree[node].l=maketree(tree[node].l,l,mid);
    tree[node].r=maketree(tree[node].r,mid+1,r);
    return node;
}
int update(int node,int l,int r,int x,int val){
    node=clone(node);//每次更新就是新建一个树的过程
    if(l==r) tree[node].val=val;
    else{
        int mid=l+r>>1;
        if(x<=mid) tree[node].l=update(tree[node].l,l,mid,x,val);
        else tree[node].r=update(tree[node].r,mid+1,r,x,val);
    }
    return node;
}
int query(int node,int l,int r,int x){
    if(l==r) return tree[node].val;
    else{
        int mid=l+r>>1;
        if(x<=mid) return query(tree[node].l,l,mid,x);
        else return query(tree[node].r,mid+1,r,x);
    }
}
int main(){
    //freopen("in.txt","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    root[0]=maketree(0,1,n);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&p,&op,&x);
        if(op==1){
            scanf("%d",&y);
            root[i]=update(root[p],1,n,x,y);
        }
        else{
            printf("%d\n",query(root[p],1,n,x));
            root[i]=root[p];
        }
    }
    return 0;
}

cf C C - Fault-tolerant Network

        注意一定要把所有情况理顺了再去打代码,不然会寄的很惨

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
ll t,n;
ll dis(ll a[],ll l,ll r,ll x){
    ll minn=mod;
    for(int i=l;i<=r;i++)
    minn=min(minn,abs(x-a[i]));
    return minn;
}
ll a[200005],b[200005];
int main(){
   // freopen("in.txt","r",stdin);
    scanf("%lld",&t);
    while(t--){
        scanf("%lld",&n);
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
        ll l1=dis(b,2,n,a[1]),r1=dis(b,1,n-1,a[n]);
        ll l2=dis(a,2,n,b[1]),r2=dis(a,1,n-1,b[n]);
        ll ans1=l1+l2+r1+r2;//四条线
        ll ans2=abs(a[1]-b[1])+r1+r2,
        ans3=abs(a[1]-b[n])+r1+l2,
        ans4=abs(a[n]-b[n])+l1+l2,
        ans5=abs(a[n]-b[1])+l1+r2,//以上都是三条线
        ans6=min(abs(a[1]-b[1])+abs(a[n]-b[n]),abs(a[1]-b[n])+abs(a[n]-b[1]));//两条线
        ll ans=mod;
        ans1=min(ans1,min(ans2,ans3));
        ans4=min(ans4,min(ans5,ans6));
        ans=min(ans1,ans4);
        printf("%lld\n",ans);
    }
    return 0;
}

cf D 多源点bfs  D - Nearest Excluded Points

        以前做的bfs都是一开始只有一个点入队,这次的是要多个点入队;先把给定的点做个标记,然后看看每个给定点四周是否是给定点,不是就入队列;之后就开始广搜,只要走到了给定点,那该给定点的最小距离就是当前队列pop出来的点;但如果一个给定点的四周也都是给定点呢?那就需要四周给定点的 最小距离对应的点,这时候就需要开一个from来记录一下方便转移

Educational Codeforces Round 124 D(BFS) - 知乎 (zhihu.com)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
ll n,x[200005],y[200005];
pair<ll,ll>ans[200005];
map<pair<ll,ll>,ll>id,vis;
map<pair<ll,ll>,pair<ll,ll>>from;
ll dx[5]={0,0,1,-1},dy[5]={1,-1,0,0};
int main(){
    //freopen("in.txt","r",stdin);

    scanf("%lld",&n);
    queue<pair<ll,ll>>q;
    for(int i=1;i<=n;i++){
        scanf("%lld%lld",&x[i],&y[i]);
        id[{x[i],y[i]}]=i;
    }

    for(int i=1;i<=n;i++)
    for(int j=0;j<4;j++){
            ll nx=x[i]+dx[j],ny=y[i]+dy[j];
            if(!id.count({nx,ny})){
                q.push({nx,ny});
                from[{nx,ny}]={nx,ny};
            }
        }

    while(!q.empty()){
        ll x=q.front().first,y=q.front().second;q.pop();
        for(int i=0;i<4;i++){
            ll nx=x+dx[i],ny=y+dy[i];
            if(id.count({nx,ny})&&!vis[{nx,ny}]){
                vis[{nx,ny}]=1;
                from[{nx,ny}]=from[{x,y}];//如果一个给定的点四周都是给定的点,
                //那就需要四周的点慢慢转移到外面的点,from就是起这么个作用
                q.push({nx,ny});
                ans[id[{nx,ny}]]=from[{nx,ny}];
            }
        }
    }
    for(int i=1;i<=n;i++) printf("%lld %lld\n",ans[i].first,ans[i].second);
    return 0;
}

cf 最短路次短路路径长度和条数 G - Counting Shortcuts

求最短路和次短路的路径长度和条数

 最短路径计数 与 次短路径计数 - 知乎 (zhihu.com)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
const ll inf=0x3f3f3f3f;
ll n,s,t,m,vis[200005][2],dist[200005][2],cnt[200005][2],T;
ll head[900005],lcnt;
struct Edge{
    ll dis,from,to,next;
}edge[900005];
void addedge(ll from,ll to,ll w){
    edge[++lcnt].from=from;
    edge[lcnt].to=to;
    edge[lcnt].dis=w;
    edge[lcnt].next=head[from];
    head[from]=lcnt;
}
struct node{
    ll id,dis,type;
    bool operator<(const node &a)const{
    return a.dis<dis;
    }
};

void dij(){
    for (int i = 0; i <= n; i++)
		vis[i][0] = vis[i][1] = cnt[i][0] = cnt[i][1] = 0, dist[i][0] = dist[i][1] = inf;
    priority_queue<node>q;
    q.push(node{s,0,0});
    dist[s][0]=0;cnt[s][0]=1;
    while(!q.empty()){
        ll u=q.top().id,ct=cnt[q.top().id][q.top().type],typ=q.top().type,distan=q.top().dis;
        q.pop();
        if(vis[u][typ]) continue;
        vis[u][typ]=1;
        for(int i=head[u];i;i=edge[i].next){
            ll j=edge[i].to;
            if(distan+edge[i].dis<dist[j][0]){
                dist[j][1]=dist[j][0];cnt[j][1]=cnt[j][0];
                q.push(node{j,dist[j][1],1});
                dist[j][0]=distan+edge[i].dis;cnt[j][0]=ct;
                q.push(node{j,dist[j][0],0});
            }
            else if(dist[j][0]==distan+edge[i].dis){
                cnt[j][0]=(cnt[j][0]+ct)%mod;
            }
            else if(dist[j][1]>distan+edge[i].dis){
                dist[j][1]=distan+edge[i].dis;cnt[j][1]=ct;
                q.push(node{j,dist[j][1],1});
            }
            else if(dist[j][1]==distan+edge[i].dis){
                cnt[j][1]=(cnt[j][1]+ct)%mod;
            }
        }
    }
    ll ans=cnt[t][0];
    if(dist[t][0]==dist[t][1]-1) ans=(ans+cnt[t][1])%mod;
    printf("%lld\n",ans);
}
int main(){
    //freopen("in.txt","r",stdin);
    scanf("%lld",&T);
    while(T--){
        for(int i=0;i<=lcnt;i++) edge[i].dis=edge[i].from=edge[i].next=edge[i].to=0;
        for(int i=1;i<=300000;i++) head[i]=0;
        lcnt=0;
        lcnt=0;
        scanf("%lld%lld",&n,&m);
        scanf("%lld%lld",&s,&t);
        for(int i=1;i<=m;i++){
            ll u,v;
            scanf("%lld%lld",&u,&v);
            addedge(u,v,1);
            addedge(v,u,1);
        }
        dij();
    }
    return 0;
}

双向排列   大思维,手写栈

设p=0为前缀操作,p=1为后缀操作;若有连续的前缀操作的话,一定是q最大的发挥了作用;同理若有连续的后缀操作的话一定是q最小的发挥作用;

 模拟一下慢慢的就会变成这个样子,红色代表这个区间要进行翻转;要变得只有红色部分,而且红色部分在逐渐缩小,蓝色部分已经确定了,就可以在蓝色区间填数了;

如果第i次是前缀,第i+1次是后缀,第i+2次是前缀而且第i+2次的后缀要大于第i+1次的右端点了,那么前两次就没啥用了

因为最后一次的区间涵盖了前两次了,无论前两个区间右端点之前的数怎样排列,在最后一个区间排序后就变为有序的了;

第一次操作一定是前缀操作,因为后缀是升序,不会改变区间;

第十二届蓝桥杯C++ B组讲解_哔哩哔哩_bilibili

#include<bits/stdc++.h>
#define ll long long
#define x first
#define y second
using namespace std;
const int mod=1e9+7;
ll n,m,a[100010],top=0,q,p;
pair<ll,ll>stk[100010];
int main(){
    //freopen("in.txt","r",stdin);
    scanf("%lld%lld",&n,&m);
    while(m--){
        scanf("%lld%lld",&p,&q);
        if(!p){
            while(top&&stk[top].x==0) q=max(q,stk[top--].y);//找到了最大的那个区间
            while(top>=2&&stk[top-1].y<=q) top-=2;//当前区间涵盖了前两个区间
            stk[++top]={p,q};
        }
        else if(top){
            while(top&&stk[top].x==1) q=min(q,stk[top--].y);
            while(top>=2&&stk[top-1].y>=q) top-=2;
            stk[++top]={p,q};
        }
    }
    ll l=1,r=n,k=n;
    for(int i=1;i<=top;i++){
        if(stk[i].x==0){
            while(l<=r&&r>stk[i].y) a[r--]=k--;
        }
        else {
            while(l<=r&&l<stk[i].y) a[l++]=k--;
        }
        if(l>r) break;
    }
    //下面这个地方如果不理解的话可以去模拟下m=1的情况
    if(top%2){//如果没填完的话还要继续填,奇数次说明该排前面的蓝色了
        while(l<=r) a[l++]=k--;
    }
    else {
        while(l<=r) a[r--]=k--;
    }
    for(int i=1;i<=n;i++) printf("%lld ",a[i]);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

killer_queen4804

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值