2024.11.05 刷題訓練
P7054 NWRRC2015 Graph
構造題,把拓撲序中的佇列換成小根堆是最小字典序,此時設定一個大根堆,用於處理連邊問題。
設 \(lst\) 是上一個拓撲序中的節點。
- 小根堆堆頂大於大根堆,當前位置最優解,不耗費連邊數量。
- 小根堆堆頂小於大根堆,若 \(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");
}
}