2019牛客多校训练(四)
比赛链接:
https://ac.nowcoder.com/acm/contest/884#question
A. meeting
题意:
给出一颗树
让所有染色点到某个点的最大距离最小
分析:
结果为最远点对的距离除二向上取整
假设有最远点对的路径上的中间点
如果有某个点它到这个中间点要远,那么最远点对就不是最远点对了,产生矛盾
利用树上点对路径唯一性,两次$dfs$求出染色点直径
ac代码:
#include<bits/stdc++.h> #define ll long long #define pa pair<int,int> using namespace std; const int maxn=1e5+10; const ll mod=1e9+7; struct Edge{ int to,nex; }edge[maxn*2]; int n,k,edge_num,head[maxn],dis[maxn],cor[maxn]; void add_edge(int u,int v) { edge_num++; edge[edge_num].nex=head[u]; edge[edge_num].to=v; head[u]=edge_num; } void dfs(int x){ for(int i=head[x];i;i=edge[i].nex){ int to=edge[i].to; if(dis[to]>dis[x]+1)dis[to]=dis[x]+1,dfs(to); } } int fin(int x) { for(int i=1;i<=n;i++)dis[i]=1e9; dis[x]=0; dfs(x); int maxx=0,inde; for(int i=1;i<=k;i++) if(maxx<dis[cor[i]])maxx=dis[cor[i]],inde=cor[i]; //for(int i=1;i<=k;i++)cout<<dis[cor[i]]<<" "; //cout<<endl; return inde; } int main() { scanf("%d %d",&n,&k); for(int i=1;i<=n-1;i++){ int u,v; scanf("%d %d",&u,&v); add_edge(u,v); add_edge(v,u); } for(int i=1;i<=k;i++)scanf("%d",&cor[i]); // cout<<k<<endl; int node1=fin(cor[1]); int node2=fin(node1); // cout<<node1<<" "<<node2<<endl; printf("%d\n",(dis[node2]+1)/2); return 0; }
D. triples I
题意:
用尽可能少的三的倍数异或出$a$
分析:
典型的构造题
首先,$2^{n}\%3$等于1或者2
1.如果$a\%3=0$,直接输出$a$
2.如果$a\%3=1$,去掉二进制某些位置,让$a$变成3的倍数即可
3.$a\%3=1$同理
ac代码:
#include<bits/stdc++.h> #define ll long long #define pa pair<int,int> using namespace std; const int maxn=3e6+10; const ll mod=1e9+7; vector<ll>ve1,ve2; int main() { // cout<<(48|24)<<endl; //cout<<((ll)1<<62)<<endl; int T; scanf("%d",&T); while(T--){ ll a; scanf("%lld",&a); if(a%3==0){ printf("1 %lld\n",a); continue; } ve1.clear(),ve2.clear(); for(int i=0;i<=62;i++){ if(a&((ll)1<<i)){ if(((ll)1<<i)%3==2)ve2.push_back((ll)1<<i); else ve1.push_back((ll)1<<i); } } printf("2 "); if(a%3==1){ if(ve1.size()>=2){ printf("%lld %lld\n",a-ve1[0],a-ve1[1]); }else if(ve1.size()==1){ printf("%lld %lld\n",a-ve1[0],a-ve2[0]-ve2[1]); }else if(ve1.size()==0){ printf("%lld %lld\n",a-ve2[0]-ve2[1],a-ve2[2]-ve2[3]); } } else { if(ve2.size()>=2){ printf("%lld %lld\n",a-ve2[0],a-ve2[1]); }else if(ve2.size()==1){ printf("%lld %lld\n",a-ve2[0],a-ve1[0]-ve1[1]); }else if(ve2.size()==0){ printf("%lld %lld\n",a-ve1[0]-ve1[1],a-ve1[2]-ve1[3]); } } } return 0; } triples I
J. free
题意:
给出一个图,最多让$k$条边花费变成0,求$s$到$t$的最短距离
分析:
对整个图分层,分成$k+1$层,第0层节点编号是$1$到$n$,第二次是$n+1$到$n+n$
建立层之间的边,并且允许第i层的点用0花费到达$i+1$层
调用迪杰斯特拉,最后的结果是$dis[k*n+t]$
ac代码:
#include<bits/stdc++.h> #define ll long long #define pa pair<int,int> using namespace std; const int maxn=2e6+10; const ll mod=1e9+7; int n,m,s,t,k,head[maxn],edge_num,dis[maxn]; bool vis[maxn]; struct Edge{ int to,w,nex; }edge[maxn*5]; void add_edge(int u,int v,int w) { // cout<<"adge:"<<u<<" "<<v<<" "<<w<<endl; edge_num++; edge[edge_num].nex=head[u]; edge[edge_num].to=v; edge[edge_num].w=w; head[u]=edge_num; } void dj() { for(int i=1;i<=k*n+n;i++)dis[i]=1e9,vis[i]=0; priority_queue<pa>que; dis[s]=0; que.push(make_pair(0,s)); while(que.size()){ pa now=que.top(); que.pop(); if(vis[now.second])continue; vis[now.second]=1; // cout<<now.second<<endl; for(int i=head[now.second];i;i=edge[i].nex){ int to=edge[i].to; int w=edge[i].w; if(dis[to]>dis[now.second]+w){ dis[to]=dis[now.second]+w; que.push(make_pair(-dis[to],to)); } } } } int main() { scanf("%d %d %d %d %d",&n,&m,&s,&t,&k); for(int i=1;i<=m;i++){ int u,v,w; scanf("%d %d %d",&u,&v,&w); for(int j=0;j<=k;j++){ add_edge(u+j*n,v+j*n,w); add_edge(v+j*n,u+j*n,w); if(j!=k){ add_edge(u+j*n,v+(j+1)*n,0); add_edge(v+j*n,u+(j+1)*n,0); } } } dj(); printf("%d\n",dis[k*n+t]); return 0; }