智障行為+2
T1 | T2 | T3 | T4 |
---|---|---|---|
0 | 0 | 0 | 0 |
好吧至少下一次不會考更低了
T1
你有個 n 個點 m 條邊的無向圖,每條邊都有紅藍兩種顏色中的一種,保證紅色的邊形成了這個圖的一個生成樹。
你希望給這些邊賦上邊權,保證邊權是 1 ∼ m 的排列,使得紅色的邊是最小生成樹。
希望這些邊權形成的序列字典序最小,也就是先比較第一條邊的邊權,再比較第二條邊的邊權,依次類推。
對於所有資料,保證 n, m ≤ 5 × 10^5 , m ≥ n − 1。
對,這道題(感覺像綠)卡了我很久,掛了
Time:2s
Memory:128M
正確思路:
首先,我們先看一張圖:
綠字代表加入順序(邊序)
根據題意,我們會發現:紅邊會構成一棵樹,之後每加入一條藍邊就會構成一個環(除了這條邊其他環上邊均為紅邊),由於藍邊一定是環上最大邊(紅邊為最小生成樹上的邊,一定比他小),所以我們就可以用拓撲排序解決此題(時間複雜度n方,TLE)
我們可以嘗試對上述方法用資料結構進行最佳化,但時間複雜度還是會有問題(線段樹時間可能沒問題,but MLE)
我們可以考慮從前往後,按順序貪心確定每條邊的大小。對於一條非樹邊,考慮有k條它對應的樹邊沒
確定,那麼把這些樹邊按順序從小到大賦值,然後把這條樹邊賦上環上最大紅邊的邊權+1即可。因為一條邊被賦完值之
後不會再被修改,所以可以用並查集實現,也就是確定的邊直接縮起來,時間複雜度O(nlogn) 。
code:
#include <bits/stdc++.h>
// #include<windows.h>
using namespace std;
// #pragma GCC optimize(2)
#define int long long
#define pii pair<int, int>
#define il inline
#define p_q priority_queue
#define map unordered_map
#define rg register
const int N1 = 100005;
const int N2 = 1000006;
const int mod = 998244353;
#define debug 0
#define endl '\n'
int _test_ = 1;
int n,m;
vector<pii> a[N1*5];
struct edge{
int x,y,col;
}e[N2];
int fa[N1*5],deep[N1*5];
int tot=0;
int top[N2],ans[N2];
int fid[N1*5];// 用來存這個節點連向父親的邊的id
namespace third_coming {
void dfs(int x,int f,int dep){
fa[x]=f;
deep[x]=dep;
for(int i=0;i<a[x].size();i++){
if(f==a[x][i].first){
continue;
}
fid[a[x][i].first]=a[x][i].second;
dfs(a[x][i].first,x,dep+1);
}
}
int asdf[5*N1];
void add_edge(int id){
if(e[id].col){//紅邊
if(!ans[id]) ans[id]=++tot;//沒有記錄過ans
return;//紅邊不需要判斷環
}
int x=e[id].x,y=e[id].y;
int u=x,v=y,tt=0;//tt,asdf是用來記錄環上沒有ans的紅邊的id
while(top[u]!=top[v]){
// cout<<u<<' '<<v<<endl;
// cout<<top[u]<<' '<<top[v]<<endl;
// cout<<fa[u]<<' '<<fa[v]<<endl;
// Sleep(100);
if(deep[top[u]]>deep[top[v]]){
if(!ans[fid[top[u]]]) asdf[++tt]=fid[top[u]];
u=fa[top[u]];
}//u比v深,u往上爬
else{
if(!ans[fid[top[v]]]) asdf[++tt]=fid[top[v]];
v=fa[top[v]];
}//v比u深,v往上爬
}//LCA+環上計算紅邊
sort(asdf+1,asdf+tt+1);
for(int i=1;i<=tt;i++){
int x=asdf[i];
// pq.pop();
ans[x]=++tot;
}
ans[id]=++tot;
int p=u,q=v;
u=x,v=y;
while(top[u]!=top[v]){
if(deep[top[u]]>deep[top[v]]){
// int p=top[u];
int x=u;
u=fa[top[u]];
top[x]=top[p];
}
else{
int x=v;
v=fa[top[v]];
top[x]=top[q];
}
}//路徑壓縮
}
void init() {
// cout<<endl;
cin>>n>>m;
for(int i=1;i<=m;i++){
// int x,y,z;
cin>>e[i].x>>e[i].y>>e[i].col;
if(!e[i].col) continue;
a[e[i].x].push_back({e[i].y,i});
a[e[i].y].push_back({e[i].x,i});
}
dfs(e[1].x,0,1);//預處理
for(int i=1;i<=n;i++){
top[i]=i;
}//top用於LCA的運算(最佳化)
for(int i=1;i<=m;i++){
add_edge(i);
}//一條一條加進去算
for(int i=1;i<=m;i++){
cout<<ans[i]<<" ";
}
}
void solve() {
}
void main() {
init();
solve();
}
}
using namespace third_coming;
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
#ifdef debug
// freopen("series.in", "r", stdin);
// freopen("series.out", "w", stdout);
#endif
third_coming::main();
return 0;
}
T2
現在有 n 個區間 [li, ri],每個區間有個權值 wi。我們把這 n 個區間當成 n 個點,如果兩個區間它們之間有交(包括端點),那麼我們就在這兩個區間之間連邊,形成了一個區間圖。
現在希望你刪除一些區間,使得每個連通塊大小不超過 k。輸出刪除區間最小的權值和。
保證 1 ≤ k ≤ n ≤ 2500, 1 ≤ li ≤ ri ≤ 10^9(區間左右端點), 1 ≤ wi ≤ 10^9。
Time:1s
Memory:1G