NIKKEI Programming Contest 2019(solve 5/6)

题目链接:https://atcoder.jp/contests/nikkei2019-qual/tasks

官方题解:https://img.atcoder.jp/nikkei2019-qual/editorial.pdf

A题

题意:n次询问,2个问题。对应统计的结果,问你2个问题都回答是yes的最多和最小有几个人。

思想:最多肯定是min(a,b)最小肯定是多出来的那些人就是A+B 2个问题超过了N,那肯定是最小的,但要考虑下不超过N.

B题

题意:给你3个字符串让三个字符串一样最少的修改次数。

思想:对于同一个位置,找一样次数最多的就行。

C题

题意:给你n种菜,A吃了A获得Ai点valu,B吃了B获得Bi点valu,一个菜只能一个人吃,问你max(sum(Ai)-sum(Bj))(i代表A吃的所有的,同理j代表B吃的所有的。i∩j=∅)

思想:对于Bi来说肯定是减去的,Ai肯定是加上的。那么你求的max(sum(Ai)-sum(Bj))。假如刚开始让A全部吃掉,那么ans=-sum(Bi)

但是A肯定会吃东西,那么A吃东西获得的valu等于Ai+Bi.因此就变成了将Ai+Bi从大到小排序贪心(跳着去,因为B也会吃)取即可。

(ps:刚开始求Bi的和取反)

D题

题意:给你n个点,n-1+m条边的有向图,让你求一颗有根树,包含n个点的树,输出每个节点的父亲节点的编号,如果当前是根输出0.

思想:通过拓扑排序的思想。先通过度求出来根是那个。然后dfs这棵树,找到一个拓扑序列,可以找到一颗符合题意的树。

const int maxn=1e5+10;
vector<int>g[maxn];
int vis[maxn];
int deg[maxn];
int fa[maxn];
void dfs(int root,int f)
{
	fa[root]=f;
	vis[root]=1;
	for(int i=0;i<g[root].size();i++)
		deg[g[root][i]]--;
	for(int i=0;i<g[root].size();i++)
	{
		if(!vis[g[root][i]] && !deg[g[root][i]])
			dfs(g[root][i],root);
	}
} 
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=0;i<n+m-1;i++)
	{
		int u,v;
		cin>>u>>v;
		g[u].push_back(v);
		vis[v]=1;
		deg[v]++;
	}
	int root=0;
	for(int i=1;i<=n;i++)
		if(!vis[i])
			root=i;
	memset(vis,0,sizeof(vis));
	dfs(root,0);
	for(int i=1;i<=n;i++)
		cout<<fa[i]<<"\n";
	return 0;
} 

E题

题意:给你n个节点,m条边,每个节点都有一个值,每条边都有一个值。问最少删多少边能够满足当前联通块权值的和大于联通块任何一条边的权值。

思想:并查集倒着来的裸题,删边不容易,那么建边,刚开始按照边的权值从小到大排序。对于要加入的边,先判断是否在联通块内,不在的话就考虑加入到一个联通块内,同时还需要记录有多少条边对于当前联通块的权值不满足题意要求的大于边的权值。

当当前联通块的权值大于等于当前边所需要的权值的时候,那么这个联通块前边不满足的边现在也满足了,因为边权是按照从小到大排序的。

typedef long long ll;
const int maxn=1e5+10;
ll valu[maxn];
int fa[maxn];
int fs[maxn];
struct node{
	int u;
	int v;
	ll valu;
}no[maxn];
bool cmp(node a,node b)
{
	return a.valu<b.valu;
}
int find(int x)
{
	if(fa[x]==x)
		return x;
	return fa[x]=find(fa[x]);
}
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>valu[i];
	for(int i=0;i<m;i++)
		cin>>no[i].u>>no[i].v>>no[i].valu;
	sort(no,no+m,cmp);
	for(int i=1;i<=n;i++)
		fa[i]=i;
	for(int i=0;i<m;i++)
	{
		int f1=find(no[i].u);
		int f2=find(no[i].v);
		if(f1!=f2)
		{
			fa[f1]=f2;
			valu[f2]+=valu[f1];
			fs[f2]+=fs[f1];
		}
		fs[f2]++;
		if(valu[f2]>=no[i].valu)
			fs[f2]=0;
	}
	ll ans=0;
	for(int i=1;i<=n;i++)
		if(find(i)==i)
			ans+=fs[i];
	cout<<ans<<"\n";
	return 0;
} 

以上仅是个人拙见,详情看官方题解,如有不对欢迎指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值