图论水题5
cf796D
题意
n个点n-1条边,k个特殊点以及整数d,要求删除最多的边保证每个点都可以在d步之内到达一个特殊点,输入保证每个点都可以在d步内到达特殊点
思路
考虑什么时候可以删除一条边,即这条边连接的两个点可以在d步内到达两个不同的特殊点,那么删除这条边仍然保证合法,可以用bfs处理出每个点最近的特殊点,如果一条边连接的两个点所最近的特殊点不同则计算答案
代码
#include<bits/stdc++.h>#define ull unsigned long long
#define ll long long
#define inf 1e9
#define INF 1e18
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define i128 __int128using namespace std;
const double eps=1e-6;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0)); const int N=3e5+10;
vector<int>e[N];void solve()
{int n,k,d;cin>>n>>k>>d;set<int>s;for(int i=1;i<=k;i++) {int x;cin>>x;s.insert(x);}vector<PII>edge(n);for(int i=1;i<n;i++){int a,b;cin>>a>>b;e[a].pb(b);e[b].pb(a);edge[i]={a,b};}queue<PII>q;for(auto t:s) q.push({t,t});vector<int>vis(n+1);while(!q.empty()){auto [cur,color]=q.front();q.pop();if(vis[cur]) continue;vis[cur]=color;for(auto ed:e[cur]) q.push({ed,color});}int cnt=0;vector<int>ans;for(int i=1;i<n;i++){auto [x,y]=edge[i];//cout<<i<<" "<<vis[x]<<" "<<vis[y]<<endl;if(vis[x]!=vis[y]){cnt++;ans.pb(i);}}cout<<cnt<<endl;for(auto t:ans) cout<<t<<" ";
}int main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);solve();return 0;
}
cf1304D
题意
给定一个由>,<>,<>,<组成字符串s,sis_isi描述了排列中ai,ai+1a_i,a_{i+1}ai,ai+1的小大关系,构造两个满足所有大小关系的排列,并使得第一个排列的lis最小,第二个排列的lis最大
思路
对于lis最小的排列,可以先按n,n−1,n−2,...1n,n-1,n-2,...1n,n−1,n−2,...1排列,那么对于一段连续的<<<,我们翻转这一段,仍然满足对于任意的i<ji<ji<j除非位于段内,否则仍然满足ai>aja_i>a_jai>aj
对于lis最大的排列,先按1,2,3,..n−1,n1,2,3,..n-1,n1,2,3,..n−1,n排列,翻转每一段>>>即可
代码
#include<bits/stdc++.h>#define ull unsigned long long
#define ll long long
#define inf 1e9
#define INF 1e18
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define i128 __int128using namespace std;
const double eps=1e-6;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0)); void solve()
{int n;cin>>n;string s;cin>>s;s=" "+s;vector<int>a(n+1),b(n+1);for(int i=1;i<=n;i++){a[i]=n-i+1;b[i]=i;}for(int i=1;i<n;i++){if(s[i]=='<'){int j=i;while(j<n && s[j]=='<') j++;reverse(a.begin()+i,a.begin()+j+1);i=j-1;}}for(int i=1;i<n;i++){if(s[i]=='>'){int j=i;while(j<n && s[j]=='>') j++;reverse(b.begin()+i,b.begin()+j+1);i=j-1;}}for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl;for(int i=1;i<=n;i++) cout<<b[i]<<" ";cout<<endl;
}int main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);int t;cin>>t;while(t--)solve();return 0;
}
cf938D
题意
有一个乐队要开演唱会,在 n 个城市的人都想去听,但每个城市票价不同,而他们都想少花钱。给定 m 条路,包含 u, v, w 表示从城市 u 到城市 v 要花 w 元,再给出演唱会在 i 城市的票价 a[i],求每个人去听演唱会并回来的最小花费。
思路
对于点权,可以将其转化为边权,具体操作为对每个点建一个虚点连边,边权即点权,由于终点不确定,可以建立一个超级虚拟源点代表终点,并向所有虚点连边权为0的边,由于题目要求往返,所以m条边的边权要乘2,处理完之后从超级源点跑最短路即可
代码
#include<bits/stdc++.h>#define ull unsigned long long
#define ll long long
#define inf 1e9
#define INF 1e18
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define i128 __int128using namespace std;
const double eps=1e-6;
typedef pair<ll,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0)); const int N=2e5+10;
struct edge{ll v,w;
};
vector<edge>e[N<<2];void solve()
{int n,m;cin>>n>>m;for(int i=1;i<=m;i++){ll a,b,c;cin>>a>>b>>c;e[a].pb({b,2*c});e[b].pb({a,2*c});}vector<ll>a(n+1);for(int i=1;i<=n;i++) {cin>>a[i];e[i].pb({i+n,a[i]});e[i+n].pb({i,a[i]});e[i+n].pb({0,0});e[0].pb({i+n,0});}vector<ll>dis(2*n+1,1e18);vector<int>vis(2*n+1);priority_queue<PII,vector<PII>,greater<PII>>q;q.push({0,0});dis[0]=0;while(!q.empty()){auto [cost,cur]=q.top();q.pop();if(vis[cur]) continue;vis[cur]=1;for(auto [v,w]:e[cur]){if(cost+w<dis[v]){dis[v]=cost+w;q.push({dis[v],v});}}}for(int i=1;i<=n;i++) cout<<dis[i]<<" ";}int main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);solve();return 0;
}
cf343D
题意
给定一棵树,有以下三种操作
- 将x的子树打上标记
- 将x及其所有祖先的标记清空
- 询问x点是否有标记
思路
树剖板子题,1操作对应子树赋值1,2操作对应x到根节点的简单路径赋值0,3操作对应单点查询
由于赋值时1和0都要使用,所以懒标记初值为-1
代码
#include<bits/stdc++.h>#define ull unsigned long long
#define ll long long
#define inf 1e9
#define INF 1e18
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define i128 __int128using namespace std;
const double eps=1e-6;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0)); const int N=5e5+10;
int fa[N],dep[N],siz[N];
int son[N];//重儿子
int top[N];//所在重链顶点
int dfn[N];//dfs序
int w[N];
int tot;
int n,q;
vector<int>e[N];ll tr[N<<2],add[N<<2];void pushup(int p)
{tr[p]=tr[lc]+tr[rc];return;
}void pushdown(int p,int l,int r)
{if(add[p]==-1) return;int mid=(l+r)>>1;tr[lc]=(mid-l+1)*add[p];add[lc]=add[p];tr[rc]=(r-mid)*add[p];add[rc]=add[p];add[p]=-1;
}
void build(int p,int l,int r)
{if(l==r){tr[p]=w[l];add[p]=-1;return;}int mid=(l+r)>>1;build(lc,l,mid);build(rc,mid+1,r);pushup(p);
}void update(int p,int l,int r,int ql,int qr,int x)
{if(ql<=l && r<=qr){tr[p]=(r-l+1)*x;add[p]=x;return;}pushdown(p,l,r);int mid=(l+r)>>1;if(ql<=mid) update(lc,l,mid,ql,qr,x);if(qr>mid) update(rc,mid+1,r,ql,qr,x);pushup(p);
}ll query(int p,int l,int r,int ql,int qr)
{if(ql<=l && r<=qr) return tr[p];pushdown(p,l,r);int mid=(l+r)>>1;ll sum=0;if(ql<=mid) sum+=query(lc,l,mid,ql,qr);if(qr>mid) sum+=query(rc,mid+1,r,ql,qr);return sum;
}void update_path(int u,int v,int x)
{while(top[u]!=top[v]){if(dep[top[u]]<dep[top[v]]) swap(u,v);update(1,1,n,dfn[top[u]],dfn[u],x);u=fa[top[u]];}if(dep[u]<dep[v]) swap(u,v);update(1,1,n,dfn[v],dfn[u],x);
}ll query_path(int u,int v)
{ll res=0;while(top[u]!=top[v]){if(dep[top[u]]<dep[top[v]]) swap(u,v);//保证u的顶点更深1res+=query(1,1,n,dfn[top[u]],dfn[u]);u=fa[top[u]];}if(dep[u]<dep[v]) swap(u,v);//最后一条链res+=query(1,1,n,dfn[v],dfn[u]);return res;
}void dfs1(int u,int father)
{fa[u]=father;dep[u]=dep[father]+1;siz[u]=1;for(auto ed:e[u]){if(ed==father) continue;dfs1(ed,u);siz[u]+=siz[ed];if(siz[son[u]]<siz[ed]) son[u]=ed;}
}void dfs2(int u,int t)
{top[u]=t;dfn[u]=++tot;if(!son[u]) return;dfs2(son[u],t);//搜重儿子for(auto ed:e[u]){if(ed==fa[u] || ed==son[u]) continue;dfs2(ed,ed);//搜轻儿子}
}void solve()
{cin>>n;for(int i=1;i<n;i++){int a,b;cin>>a>>b;e[a].pb(b);e[b].pb(a);}dfs1(1,0);dfs2(1,1);build(1,1,n);cin>>q;while(q--){int op,x;cin>>op>>x;if(op==1) update(1,1,n,dfn[x],dfn[x]+siz[x]-1,1);else if(op==2) update_path(1,x,0);else cout<<query(1,1,n,dfn[x],dfn[x])<<endl;}
}int main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);solve();return 0;
}
cf 2132F
题意
给定一张n个点m条边的无向图以及q次询问,每次询问求顶点v到其最近的必经路径的距离,必经路径指顶点1到n必须经过的边,边权都为1,若不存在必经路径输出-1
思路
- 1-n的必经路径即边双缩点后1所在连通分量到所在连通分量的割边(缩点后的边一定为割边)
- 很明显-1的情况为1和n在一个边双内,即不存在割边
- 缩点后处理出所有的必经路径,将每条必经路径的顶点跑多源bfs即可
代码
#include<bits/stdc++.h>#define ull unsigned long long
#define ll long long
#define inf 1e9
#define INF 1e18
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define i128 __int128using namespace std;
const double eps=1e-6;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0)); const int N=2e6+10;
struct edge{int to,ne;//终点,下一条边
}e[N];
int h[N];
int idx=1;
int dfn[N],low[N],tot;
stack<int>stk;int dcc[N],cnt;
int bri[N];vector<int>e1[N];void add(int a,int b)
{e[++idx].to=b;e[idx].ne=h[a];h[a]=idx;
}struct node{ll cost,id,u;bool operator < (const node&t)const{return cost!=t.cost ? cost>t.cost : id>t.id;}
};void tarjan(int x,int in_edg)
{dfn[x]=low[x]=++tot;stk.push(x);for(int i=h[x];i;i=e[i].ne){int y=e[i].to;if(!dfn[y]){tarjan(y,i);low[x]=min(low[x],low[y]);if(low[y]>dfn[x]) bri[i]=bri[i^1]=true;}else if(i!=(in_edg^1)) low[x]=min(low[x],dfn[y]);}if(dfn[x]==low[x]){++cnt;while(1){int y=stk.top();stk.pop();dcc[y]=cnt;if(y==x) break;}}
}void solve()
{int n,m;cin>>n>>m;for(int i=1;i<=n;i++){e[i]={0,0};e1[i].clear();h[i]=low[i]=dfn[i]=0;dcc[i]=bri[i]=0;}cnt=tot=0;idx=1;vector<int>x(m+1),y(m+1);for(int i=1;i<=m;i++){cin>>x[i]>>y[i];add(x[i],y[i]);add(y[i],x[i]);}tarjan(1,0);if(dcc[1]==dcc[n]){int q;cin>>q;while(q--){int c;cin>>c;cout<<-1<<" ";}cout<<endl;return;}for(int i=1;i<=m;i++){if(dcc[x[i]]!=dcc[y[i]]){e1[dcc[x[i]]].pb(dcc[y[i]]);e1[dcc[y[i]]].pb(dcc[x[i]]);}}vector<int>pre(n+1);auto dfs=[&](auto &&dfs,int u,int fa)->void{pre[u]=fa;for(auto ed:e1[u]){if(ed==fa) continue;dfs(dfs,ed,u);}};dfs(dfs,dcc[1],0);vector<int>must(n+1);//标记必经路径点int cur=dcc[n];//cout<<cur<<endl;while(cur!=dcc[1]){//cout<<cur<<endl;must[cur]=1;cur=pre[cur];}must[dcc[1]]=1;priority_queue<node>pq;for(int i=1;i<=m;i++){int u=dcc[x[i]];int v=dcc[y[i]];if(u==v) continue;if(must[u] && must[v]){//cout<<x[i]<<" "<<y[i]<<endl;pq.push({0,i,x[i]});pq.push({0,i,y[i]});}}vector<int>ans(n+1);while(!pq.empty()){auto [cost,id,cur]=pq.top();pq.pop();if(ans[cur]) continue;ans[cur]=id;for(int i=h[cur];i;i=e[i].ne){int y=e[i].to;if(ans[y]) continue;pq.push({cost+1,id,y});}}int q;cin>>q;while(q--){int c;cin>>c;cout<<ans[c]<<" ";}cout<<endl;
}int main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);int t;cin>>t;while(t--)solve();return 0;
}
cf1436D
题意
一棵树,每个点的权值可以分给它的子节点,求使得叶子节点中权值的最大值最小的值
思路
- 对于一颗权值和为sizsizsiz,叶子数为cntcntcnt的子树,使最大值最小的分法显然为⌈sizcnt⌉\lceil \frac{siz}{cnt} \rceil⌈cntsiz⌉,即让叶子节点均分总权值,但是若存在一个叶子节点的权值siz[u]>⌈sizcnt⌉siz[u]>\lceil \frac{siz}{cnt} \rceilsiz[u]>⌈cntsiz⌉,那么此时叶子节点的最大值就为siz[u]siz[u]siz[u]
- 根据以上可得到dp[u]=max(siz[v],⌈sizcnt⌉)dp[u]=max(siz[v],\lceil \frac{siz}{cnt} \rceil)dp[u]=max(siz[v],⌈cntsiz⌉),其中siz[v]siz[v]siz[v]为u为根的最大叶子节点权值
#include<bits/stdc++.h>#define ull unsigned long long
#define ll long long
#define inf 1e9
#define INF 1e18
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define i128 __int128using namespace std;
const double eps=1e-6;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0)); const int N=2e5+10;
vector<int>e[N];void solve()
{int n;cin>>n;for(int i=2;i<=n;i++){int x;cin>>x;e[x].pb(i);}vector<ll>dp(n+1);//i为根的子树答案vector<ll>cnt(n+1);//叶子vector<ll>siz(n+1);//子树权值for(int i=1;i<=n;i++) cin>>siz[i];for(int i=1;i<=n;i++) if(!e[i].size()) cnt[i]=1;auto dfs=[&](auto &&dfs,int u)->void{for(auto ed:e[u]){dfs(dfs,ed);siz[u]+=siz[ed];cnt[u]+=cnt[ed];dp[u]=max(dp[u],dp[ed]);}dp[u]=max(dp[u],(siz[u]+cnt[u]-1)/cnt[u]);};dfs(dfs,1);cout<<dp[1]<<endl;
}int main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);solve();return 0;
}