2019哈尔滨-Exchanging Gifts-(思维贪心+拓扑序路径)

60 篇文章 1 订阅
54 篇文章 2 订阅

E

题意:
就是给你n次操作,一种是给你直接给你一个序列,一种是这个序列为前面的某两个a和b序列拼接起来。现在问你第n个序列,这个序列是一些值,这个序列再随意排列后再相应匹配,问你最多有多少个人满足他目前的数和处理后的数不一样。

思考:
假如你有两个序列,排序后最多有多少人的值不同。这个就是一个数学结论,比如出现次数最大的次数为n,剩下的总和为m,如果n>m那么答案就是2*m,否则就是n+m。当n>m的时候,左边和右边相互匹配。当n<m的时候,那么就可以迭代的替代,第一大的代替第二大的,依次类推,最终就可以得到n+m人都不一样。
那么现在就是处理怎么得到这些数了。注意到要么是一个序列,要么是前面的某两个序列拼接起来,但是拼接拼接最后这个数组很大啊,不可能直接取枚举一遍。所以就转化成对于第n个序列,到底是由多少个基础序列得到的就这样就可以了。刚开始用了个dfs直接从n开始搜,但是复杂度高超时了。然后就画画图发现dfs不止一遍,所以现在就是从n点到叶子节点由多少条路径的问题,这不就是拓扑记录方案数吗。然后直接拓扑序一遍,但是又发现有些点的入读不可能变成0,这个就要注意了,刚开始入队的时候,让所有in为0的进去就行了,这样才能保证每个点都会进入。只让n点初始为1就可以了。然后就完了。当然这个cf上面的评测有点慢,据说现场评测比较快。

代码:

#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define ll long long
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);

using namespace std;

const int N = 1e6+5;

int T,n;
int vis[N];
ll dist[N],in[N];

unordered_map<int,ll > mp;
vector<int > v[N],e[N];

void init()
{
	mp.clear();
	for(int i=0;i<=n;i++)
	{
		vis[i] = dist[i] = in[i] = 0;
		v[i].clear();e[i].clear();
	}	
}

void topsort()
{
	queue<int > q;
	dist[n] = 1;
	for(int i=1;i<=n;i++)
	{
		if(!in[i]) q.push(i);
	}
	while(q.size())
	{
		auto now = q.front();
		q.pop();
		for(auto spot:v[now])
		{
			dist[spot] += dist[now];
			if(--in[spot]==0) q.push(spot);
		}
	}
}

signed main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		init();
		for(int i=1;i<=n;i++)
		{
			int op,m,x;
			scanf("%d",&op);
			if(op==1)
			{
				scanf("%d",&m);
				vis[i] = 1;
				while(m--)
				{
					scanf("%d",&x);
					e[i].pb(x);
				}
			}
			else
			{
				int a,b;
				scanf("%d%d",&a,&b);
				v[i].pb(a);v[i].pb(b);
				in[a]++,in[b]++;
			}
		}
		topsort();
		for(int i=1;i<=n;i++)
		{
			if(!vis[i]) continue;
			for(auto t:e[i]) mp[t] += dist[i];
		}
		ll maxn = 0,sum = 0;
		for(auto t:mp)
		{
			maxn = max(maxn,t.se);
			sum += t.se;
		}
		if(maxn>sum-maxn) printf("%lld\n",2*(sum-maxn)); 
		else printf("%lld\n",sum);
	}
	return 0;
}

总结:
多多思考多多培养思维把。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值