洛谷 P5163 WD与地图 题解

本文深入解析了一种复杂的图算法,通过实例演示了如何处理大规模有向图的数据优化问题,包括边的废弃、城市发达程度的更新及地区发达城市总和的查询。涉及离线处理、并查集、链表、集合、栈、递归、二分查找树等数据结构与算法。

题目传送门

题目背景

WD整日沉浸在地图中,无法自拔……

题目描述

CX让WD研究的地图可以看做是n个点,m条边的有向图,由于政府正在尝试优化人民生活,他们会废弃一些无用的道路来把省下的钱用于经济建设。

城市都有各自的发达程度s_i 。
为了方便管理,政府将整个地图划分为一些地区,两个点u,vu,v在一个地区当且仅当u,vu,v可以互相到达。政府希望知道一些时刻某个地区的前kk发达城市的发达程度总和,以此推断建设的情况。

也就是说,共有三个操作:

1 a b表示政府废弃了从a连向b的边,保证这条边存在。

2 a b表示政府把钱用于建设城市a,使其发达程度增加b.

3 a b表示政府希望知道a城市所在地区发达程度前b大城市的发达程度之和。如果地区中的城市不足b个输出该地区所有城市的发达程度总和。

AC代码:

#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=4e5+10,M=N*50;
int tn,n,m,Q,cnt,val[N];
vector<ll>ans;
set<pii>E;
ll res[N];

namespace IO
{
    int read()
    {
        int ret=0;char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
        return ret;
    }
    void write(ll x)
    {
        if(x>9) write(x/10);
        putchar(x%10^48);
    }
    void writeln(ll x){write(x);puts("");}
}
using namespace IO;

namespace Segment
{
    int sz,ls[M],rs[M],cnt[M],rt[N];
    ll sum[M];
    void update(int &x,int l,int r,int p,int v)
    {
        if(!x)x=++sz;cnt[x]+=v;sum[x]+=(ll)p*v;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(p<=mid) update(ls[x],l,mid,p,v);
        else update(rs[x],mid+1,r,p,v);
    }
    ll query(int x,int l,int r,int p)
    {
        if(p>=cnt[x]) return sum[x];
        if(l==r) return sum[x]/cnt[x]*p;
        int mid=(l+r)>>1;
        if(cnt[rs[x]]>=p) return query(rs[x],mid+1,r,p);
        else return query(ls[x],l,mid,p-cnt[rs[x]])+sum[rs[x]];
    }
    int Tmerge(int x,int y)
    {
        if(!x || !y) return x+y;
        cnt[x]+=cnt[y];sum[x]+=sum[y];
        ls[x]=Tmerge(ls[x],ls[y]);
        rs[x]=Tmerge(rs[x],rs[y]);
        return x;
    }
}
using Segment::update;
using Segment::query;
using Segment::Tmerge;
using Segment::rt;

namespace DSU
{
    int fa[N];
    int findf(int x){return fa[x]==x?x:fa[x]=findf(fa[x]);}
    void merge(int x,int y)
    {
        x=findf(x);y=findf(y);
        if(x==y) return;
        if(x>y) swap(x,y);
        fa[x]=y;
    }
}
using namespace DSU;

namespace Graph
{
    int ind,tot;
    int low[N],dfn[N],ins[N],head[N];
    stack<int>st;
    struct edge
    {
        int u,v,t;
        edge(int _u=0,int _v=0,int _t=0):u(_u),v(_v),t(_t){}
    }g[N],cpy[N];
    vector<edge>mp[N];
    struct Tway
    {
        int v,nex;
        Tway(int _v=0,int _nex=0):v(_v),nex(_nex){}
    }e[N];
    void addedge(int u,int v){e[++tot]=Tway(v,head[u]);head[u]=tot;}
    void clear(int x){low[x]=dfn[x]=ins[x]=0;}
    void tarjan(int x)
    {
        dfn[x]=low[x]=++ind;ins[x]=1;st.push(x);
        for(int i=head[x];i;i=e[i].nex)
        {
            int v=e[i].v;
            if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
            else if(ins[v]) low[x]=min(low[x],dfn[v]);
        }
        if(low[x]^dfn[x]) return;
        for(int t=0;t^x;t=st.top(),st.pop()) merge(st.top(),x),ins[st.top()]=0;
    }
}
using Graph::addedge;
using Graph::tarjan;
using Graph::dfn;
using Graph::edge;
using Graph::mp;
using Graph::g;
using Graph::cpy;

namespace Divide_Conquer
{
    pii arr[N];
    void solve(int l,int r,int L,int R)
    {
        //cerr<<l<<" "<<r<<" "<<L<<" "<<R<<endl;
        if(l==r)
        { 
            for(int i=L;i<=R;++i) mp[l].pb(g[i]);
            return;
        }
        if(L==R)
        {
            mp[(findf(g[L].u)==findf(g[L].v))?g[L].t:Q+1].pb(g[L]);
            return;
        }
        int mid=(l+r)>>1,c=0;
        for(int i=L;i<=R;++i) if(g[i].t<=mid)
        {
            int u=findf(g[i].u),v=findf(g[i].v);
            arr[++c]=mkp(u,v);Graph::head[u]=Graph::head[v]=0;
        }
        Graph::tot=0;
        for(int i=1;i<=c;++i)
        {
            int u=arr[i].fi,v=arr[i].se;addedge(u,v);
            Graph::clear(u);Graph::clear(v);
        }
        //cerr<<c<<endl;
        //if(r==12500) exit(0);
        Graph::ind=0;
        while(!Graph::st.empty()) Graph::st.pop();
        for(int i=1;i<=c;++i)
        {
            if(!dfn[arr[i].fi]) tarjan(arr[i].fi);
            if(!dfn[arr[i].se]) tarjan(arr[i].se);
        }
        int tl=0,tr=L,t=1;vector<pii> cp;
        for(int i=L;i<=R;++i)
        {
            int fg=0;
            if(g[i].t<=mid)
            {
                int u=arr[t].fi,v=arr[t].se;++t;
                if(findf(u)==findf(v)) fg=1;
                cp.pb(mkp(u,fa[u]));cp.pb(mkp(v,fa[v]));
            }
            if(fg) g[tr++]=g[i]; else cpy[++tl]=g[i];
        }
        for(int i=tr;i<=R;++i) g[i]=cpy[i-tr+1];
        for(int i=1;i<=c;++i) fa[arr[i].fi]=arr[i].fi,fa[arr[i].se]=arr[i].se;
        if(L<tr) solve(l,mid,L,tr-1);
        for(vector<pii>::iterator it=cp.begin();it!=cp.end();++it) fa[(*it).fi]=(*it).se;
        //exit(0);
        if(tr<=R) solve(mid+1,r,tr,R);
    }
}

struct Tquery
{
    int t,a,b;
    Tquery(int _t=0,int _a=0,int _b=0):t(_t),a(_a),b(_b){}	
}qs[N];

int main()
{
#ifndef ONLINE_JUDGE
    freopen("LGP5163.in","r",stdin);
    freopen("LGP5163.out","w",stdout);
#endif
    n=read();m=read();Q=read();
    for(int i=1;i<=n;++i) val[i]=read(),fa[i]=i;
    for(int i=1,u,v;i<=m;++i) u=read(),v=read(),E.insert(mkp(u,v));
    for(int i=Q;i;--i)
    {
        int t=read(),a=read(),b=read();
        qs[i]=Tquery(t,a,b);
        if(t==1) g[++cnt]=edge(a,b,i),E.erase(mkp(a,b));
        else if(t==2) val[a]+=b;
    }
    for(set<pii>::iterator it=E.begin();it!=E.end();++it) g[++cnt]=edge((*it).fi,(*it).se,0);
    Divide_Conquer::solve(0,Q+1,1,m);
    for(int i=1;i<=n;++i) tn=max(tn,val[fa[i]=i]);
    for(int i=1;i<=n;++i) update(rt[i],1,tn,val[i],1);
    qs[0].t=1;
    for(int i=0;i<=Q;++i)
    {
        if(qs[i].t==1)
        {
            for(vector<edge>::iterator it=mp[i].begin();it!=mp[i].end();++it)
            {
                int u=(*it).u,v=(*it).v;
                u=findf(u);v=findf(v);
                if(u==v) continue;
                fa[v]=u;rt[u]=Tmerge(rt[u],rt[v]);
            }
        }
        else if(qs[i].t==2)
        {
            int x=findf(qs[i].a),&v=val[qs[i].a];
            update(rt[x],1,tn,v,-1);update(rt[x],1,tn,v-=qs[i].b,1);
        }
        else ans.pb(query(rt[findf(qs[i].a)],1,tn,qs[i].b));
    }
    for(int i=(int)ans.size()-1;~i;--i) writeln(ans[i]);//printf("%lld\n",ans[i]);
    return 0;
}
洛谷 P3922 题目《改变队列》(暂译名,具体题目请以平台为准)是一道涉及队列操作数据结构设计的题目。题目的核心要求是通过一系列操作来实现队列中元素的调整,以满足特定条件。 ### 问题分析 题目通常要求维护一个队列,支持以下操作: 1. **入队**:将一个元素加入队列末尾。 2. **出队**:移除队列头部的元素。 3. **查询队列中某个特定位置的元素**。 4. **对队列中的元素进行某种形式的修改**(如更新某个位置的值)。 这类问题通常需要一个既能高效进行队列操作又能支持随机访问的数据结构。标准库中的 `std::queue` 并不能很好地支持随机访问,因此可以考虑使用 `std::deque` 或者 `std::vector` 来实现更灵活的操作。 ### 解题思路 - **数据结构选择**:使用 `std::deque` 或 `std::vector` 来模拟队列,因为它们支持高效的头部和尾部操作,同时也能通过索引进行随机访问。 - **操作实现**: - 入队操作直接使用 `push_back()`。 - 出队操作可以通过维护一个偏移量或使用 `pop_front()` 来实现。 - 查询特定位置的元素可以通过索引直接访问。 - 修改操作也可以通过索引直接完成。 ### 示例代码 以下是一个基于 `std::vector` 的实现示例: ```cpp #include <iostream> #include <vector> using namespace std; int main() { int q; cin >> q; vector<int> queue; int front = 0; // 模拟队列的头部位置 for (int i = 0; i < q; ++i) { string op; cin >> op; if (op == "push") { int val; cin >> val; queue.push_back(val); } else if (op == "pop") { front++; // 模拟出队 } else if (op == "query") { int pos; cin >> pos; cout << queue[front + pos - 1] << endl; } else if (op == "modify") { int pos, val; cin >> pos >> val; queue[front + pos - 1] = val; } } return 0; } ``` ### 复杂度分析 - **入队和出队**:时间复杂度为 $O(1)$。 - **查询和修改**:由于使用了随机访问,时间复杂度也为 $O(1)$。 ### 优化建议 - 如果数据量较大,可以考虑使用 `std::deque`,它在头部和尾部的插入和删除操作效率更高。 - 对于频繁的中间位置访问和修改操作,`std::vector` 仍然是一个不错的选择,因为它的内存是连续的,访问速度较快。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值