牛客小白月赛29 DIJ题——解题报告

题目链接:

https://ac.nowcoder.com/acm/contest/8564

I题修改

题解:

比较巧妙的转换成图论的好题,思路感觉不太好像,不过感觉是个经典题() 。

因为初始的序列是随机,不妨假设序列的每一位的数都不一样,即最复杂的情况。

然后我们考虑这个操作的意义,第i次操作其实就是把a[l_{i}]的值变成a[r_{i}+1]的值,

我们可以建一个虚点a[n+1]=0。

这样操作就变成了在 l_{i} 和 r_{i}+1 建一条权值为 w_{i} 的边,具体的含义上面解释过了。

这样最终的答案不就是求我们建的图的最小生成树的权值和。。

代码:

#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (int i = (s); i <= (t); i++)
#define RP(i,t,s) for (int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define debug puts("ac")
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}
const int N = 2e5+7;
struct edge{
	int u,v,w;
}e[N];
int fa[N];
int find(int x){
	return x==fa[x]?fa[x]:fa[x]=find(fa[x]);
}
bool cmp(edge a,edge b){
	return a.w<b.w;
}
int main(){
	int n=read(),m=read();
	rp(i,1,m) e[i].u=read(),e[i].v=read()+1,e[i].w=read();
	rp(i,1,n+1) fa[i]=i;
	sort(e+1,e+1+m,cmp);
    ll ans=0;
	int cnt=0;
	rp(i,1,m){
		int u=find(e[i].u),v=find(e[i].v);
		if(cnt==n) break;
		if(u!=v){
			cnt++;
			ans+=e[i].w;
			fa[u]=fa[v];
		}
	} 
	if(cnt!=n) cout<<-1<<endl;
	else cout<<ans<<endl; 	
	return 0;
}

J题克隆

题解:因为最终要保证点至少被经过一次,而且给的 \left \lceil \frac{2n}{k}\right \rceil 这个数很有特点,我们用它乘以k不难发现>=2n的,即这k个人走过的点数和>=2n。

这时我们就需要知道一个知识,一个图的欧拉序的点数和是满足这个条件的(注意这里说的是第二种欧拉序,如果不懂得话可以参考这篇文章)。

因此我们只需要求欧拉序,然后不断的连续取\left \lceil \frac{2n}{k}\right \rceil 的点就是符合条件的了,因为欧拉序本身就能保证连续区间的点彼此也都是可以连续的。

代码:

#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (int i = (s); i <= (t); i++)
#define RP(i,t,s) for (int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define debug puts("ac")
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}
const int N = 1e5+7;
vector<int> G[N];
int vis[N];
vector<int> ans;
void dfs(int u){
	ans.p_b(u);
	vis[u]=1;
	for(auto v:G[u]){
		if(vis[v]==1) continue;
		dfs(v);
		ans.p_b(u);
	}
}
int main(){
	int n=read(),m=read(),k=read();
	rp(i,1,m) {
		int u=read(),v=read();
		G[u].p_b(v);
		G[v].p_b(u);
	}
	dfs(1);
	puts("YES");
	int num=(2*n+k-1)/k;
	int len=ans.size();
	int cur=0;
	rp(i,1,k){
		int tmp=min(cur+num-1,len-1);
		if(cur>len-1) puts("1 1");
		printf("%d ",tmp-cur+1);
		rp(j,cur,tmp) printf("%d ",ans[j]);
		puts("");
		cur=tmp+1;
	}
	return 0;
}

D题

题解:这个题的数据有点弱,不知道为啥有的明显错误的方法都给过了。。。。。

正解除了题解中给出的二分(其实是不理解)做法,另一个做法也是很好理解的。

我们推导样例不难发现,最多只能获得深度小于等于操作次数的点的子树节点的min能够在符合条件的max操作里传到根节点。

因此答案就是所有深度小于等于操作次数的子树节点的min值中的最大值。。

#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define debug puts("ac")
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}
#define M 200020
int n,m,dep[M],mn[M],val[M],ls[M],rs[M],ans=-INF;
inline void dfs(int x,int last=0){
    dep[x]=dep[last]+1;
    if(!ls[x]&&!rs[x]) mn[x]=val[x]; else mn[x]=INF;
    if(ls[x]) dfs(ls[x],x),mn[x]=min(mn[x],mn[ls[x]]);
    if(rs[x]) dfs(rs[x],x),mn[x]=min(mn[x],mn[rs[x]]);
    if(dep[x]<=((m+1)>>1)) ans=max(ans,mn[x]);
}
int main(){
    n=read(),m=(n-1)>>1;
    for(int i=1;i<=n;i++) ls[i]=read(),rs[i]=read();
    for(int i=1;i<=n;i++) val[i]=read();
    dep[0]=-1,dfs(1),printf("%d\n",ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值