2024.11.05 刷題訓練

彬彬冰激凌發表於2024-11-04

2024.11.05 刷題訓練

P7054 NWRRC2015 Graph

構造題,把拓撲序中的佇列換成小根堆是最小字典序,此時設定一個大根堆,用於處理連邊問題。

\(lst\) 是上一個拓撲序中的節點。

  1. 小根堆堆頂大於大根堆,當前位置最優解,不耗費連邊數量。
  2. 小根堆堆頂小於大根堆,若 \(k\) 不為 \(0\) 加入到大根堆中並將 \(k--\),選擇大根堆中的數時,需要從上一個拓撲序中的節點 \(lst\) 連一條有向邊到當前點。

證明顯然,將序列中靠前的位置放置了可控制範圍內最大的數字。

#include<bits/stdc++.h>
using namespace std;

#define pii pair<int,int>
#define fi first
#define se second

const int maxn=1e5+5;

int n,m,k;
int ind[maxn],ans[maxn];

vector<int>E[maxn];

vector<pii>edge;

priority_queue<int>q;
priority_queue<int,vector<int>,greater<int>>p;

int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        E[u].push_back(v);
        ind[v]++;
    }
    for(int i=1;i<=n;i++) if(!ind[i]) p.push(i);
    int lst=0;
    for(int i=1;i<=n;i++)
    {
        while(k&&!p.empty())
        {
            int tmp=-1;
            if(!q.empty()) tmp=q.top();
            if(p.top()>tmp&&p.size()==1) break;
            q.push(p.top());p.pop(),k--;
        }
        int now=0;
        if(!p.empty()) now=p.top(),p.pop();
        else now=q.top(),q.pop(),edge.push_back({lst,now});
        ans[i]=now;
        for(auto v:E[now]) if(!(--ind[v])) p.push(v);
        lst=now;
    }
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    putchar('\n');
    printf("%d\n",(int)edge.size());
    for(auto v:edge) printf("%d %d\n",v.fi,v.se);
}

P7124 Ynoi2008 stcm

又是構造,妙妙題。

把一棵子樹的 \(dfn\) 始末看作一條線段,按照線段左端點排序,滿足條件時整個序列中除了 \([l,r]\) 中的點都是加入集合的。

考慮分治,當前處理的區間為 \([l,r]\) 時區間 \([1,l-1]\cup [r+1,n]\) 中的點已經加入集合。

求出當前的區間中點 \(mid\),將所有跨過(包括碰到)\(mid\) 的線段處理出來,由於 \(dfn\) 序良好的性質,先處理上述端點靠左的線段,發現後面處理的線段一定是前一個處理線段的子線段,不斷增加集合的數即可實現上述處理。

然後遞迴至區間 \([l,mid]\)\([mid+1,r]\)

實現看程式碼:

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5+5;

struct Edge
{
    int tot;
    int head[maxn];
    struct edgenode{int to,nxt;}edge[maxn*2];
    inline void add(int x,int y)
    {
        tot++;
        edge[tot].to=y;
        edge[tot].nxt=head[x];
        head[x]=tot;
    }
}T;

int n,cok;
int dfn[maxn],ed[maxn],fdfn[maxn];

vector<int>vec;

inline void clr()
{
    for(int i=1;i<=n;i++) T.head[i]=dfn[i]=ed[i]=fdfn[i]=0;
    T.tot=cok=0;
    vec.clear();
}
inline void dfs(int u)
{
    ed[u]=dfn[u]=++cok;fdfn[cok]=u;
    vec.push_back(dfn[u]);
    for(int i=T.head[u];i;i=T.edge[i].nxt)
    {
        int v=T.edge[i].to;
        dfs(v);
        ed[u]=ed[v];
    }
}
inline void solve(int l,int r)
{
    vector<int>lv,rv;
    int ld=l-1,rd=r+1,mid=(l+r)>>1,cnt=0;
    for(auto v:vec)
    {
        if(ed[fdfn[v]]<mid) lv.push_back(v);
        else if(v>mid) rv.push_back(v);
        else
        {
            while(ld<v-1) printf("+%d",fdfn[++ld]),cnt++;
            while(rd>ed[fdfn[v]]+1) printf("+%d",fdfn[--rd]),cnt++;
            printf("=%d",fdfn[v]);
        }
    }
    if(l==r) return ;
    for(int i=1;i<=cnt;i++) printf("-");
    if(l==r) return ;
    for(int i=l;i<=mid;i++) printf("+%d",fdfn[i]);
    swap(vec,rv);
    solve(mid+1,r);
    for(int i=l;i<=mid;i++) printf("-");
    for(int i=mid+1;i<=r;i++) printf("+%d",fdfn[i]);
    swap(vec,lv);
    solve(l,mid);
    for(int i=mid+1;i<=r;i++) printf("-");
}

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        clr();
        for(int i=1;i<n;i++)
        {
            int u;
            scanf("%d",&u);
            T.add(u,i+1);
        }
        dfs(1);
        sort(vec.begin(),vec.end());
        solve(1,n);
        printf("!\n");
    }
}

相關文章