2019ccpc哈尔滨 E - Exchanging Gifts Gym 线性求解+unorderdmap

题目链接

Problem - E - Codeforces

题目大意

有两种操作 第一种是列出一个序列 第二种是合并之前出现的序列生成一个新的序列

问你最后序列的快乐值是多少

快乐值定义是 将序列随意排序后最大的与之前位置不同的元素个数

题目思路

经过观察发现

最大快乐值

数量最多的元素数量=maxv

总元素数量=tot

当maxv<tot/2时 快乐值=tot

否则快乐值 =tot-(maxv-(tot-maxv))

列出如下之图可看得更明白

有上图到下图 明显不快乐值为 最大的数量5 -剩余数量4=1

快乐值=9-1=8

问题是如何找到各个元素数量

由于每个操作2可以吸收两个序列

可以很容易想到拓补

但是该题有一个条件就是2操作的对象序号小于 i

那么该题就不需要拓扑了 直接从n到1线性求解

设置num【n】=1

若该点是操作2则将操作对象的num+=num【i】

若该点是操作1则该点包含所有的点的数量+=num【i】

若某一个点num【i】==0 就跳过

这样的时间复杂度是O(N+K)

但由于读入数量级的问题 很容易T

因此我用了一个超级快读(注意该快读无法读入其他类型)

static char buf[100000],*pa=buf,*pd=buf;
#define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
inline int read()
{
    register int x(0);register char c(gc);
    while(c<'0'||c>'9')c=gc;
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
    return x;
}

题目代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
int t,n,m,T,q,x,y;

int lef[maxn],inlj[maxn];
ll num[maxn];
ll tot,maxv;
unordered_map<int ,ll >mp;
static char buf[100000],*pa=buf,*pd=buf;
#define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
inline int read()
{
    register int x(0);register char c(gc);
    while(c<'0'||c>'9')c=gc;
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
    return x;
}
//void dfs(int floor)
//{
//	if(lef[floor])
//	for(int i=0;i<e[floor];i++)
//	{
//		
//	}
//}
vector<int >v[maxn];
int main()
{
	T=read();
	while(T--)
	{
		n=read();
		mp.clear();
		tot=maxv=0;
		
		for(int i=1;i<=n;i++)
		{
			t=read();
			v[i].clear();
			lef[i]=num[i]=0;
			if(t==1)
			{
				q=read();
				for(int j=0;j<q;j++)
				{
					x=read();
					v[i].push_back(x);
				} 
			}
			else if(t==2)
			{
				x=read();
				y=read();
				v[i].push_back(x);
				v[i].push_back(y);
				lef[i]=1;
			}
		} 
		num[n]=1;
		if(lef[n])
		{
			//dfs(n);
			for(int i=n;i>=1;i--)
			{
				if(num[i]==0)continue;
				int floor=i;
				if(lef[floor]==1)
				{
					num[v[floor][0]]+=num[i];
					num[v[floor][1]]+=num[i];
				}
				else 
				{
					for(int j=0;j<v[floor].size();j++)
					{
						int y=v[floor][j];
						mp[y]+=num[i];
						maxv=max(maxv,mp[y]);
						tot+=num[i];
					}
				}
			} 
			
		}
		else 
		{
			int floor=1;
			for(int j=0;j<v[floor].size();j++)
			{
				int y=v[floor][j];
				mp[y]+=num[floor];
				maxv=max(maxv,mp[y]);
				tot+=num[floor];
			}
		}
		if(maxv<=(tot)/2)
		{
			printf("%lld\n",tot);
		}
		else 
		{
			ll tmp=maxv-(tot-maxv);
			printf("%lld\n",tot-tmp);
		}
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值