比赛链接:
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;
}