2019CCPC哈尔滨 Exchanging Gifts 递推,map

题目链接

https://vjudge.net/problem/Gym-102394E

题意

给定n个序列,给出方式有两种:直接给定m个数字作为序列si,或者给定j,k,使si=sj+sk。

对sn任意排序,最多能使多少个位置上的数字与之前不同?

思路

先记录输入,vector数组存储第一种方式给定的序列。

从后往前递推出每一个序列的使用次数并记录。

使用unordered_map统计出现次数最多的数字,并记录序列总长度

答案为min(len,2len-2cnt)

教训/收获
  • map除了建立映射以外还保证了内部元素的有序,所以查询插入复杂度为logn,c++11支持hash原理的unordered_map查询插入复杂度为O(1)
  • 善用vector可以节省很大的空间开销,1e8 int 数组约400M
  • 有思路就写,做错好过什么都不做
代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<string>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<unordered_map>
#include<cstdlib> 
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
	typedef long long ll;
	const int maxn=1005000;
	const int inf=0x3f3f3f3f;
	int n,m;
	ll cmd[maxn][3];
	vector<ll>v[maxn];
	ll need[maxn];
	queue<ll>q;
	unordered_map<ll,ll>mp;
	template <typename T>
	void read(T &x){
    	x=0;
    	char ch=getchar();
    	ll f=1;
	    while(!isdigit(ch)){
	        if(ch=='-')
	            f*=-1;
	        ch=getchar();
	    }
	    while(isdigit(ch)){
	        x=x*10+ch-48;
	        ch=getchar();
	    }
	    x*=f;
	}
	signed main(){
		int tn;
		read(tn);
		while(tn--){
			read(n);
			for(int i=1;i<=n;i++){
				read(cmd[i][0]);
				if(cmd[i][0]==1){
					read(m);
					for(int j=0;j<m;j++){
						ll tmp;
						read(tmp);
						v[i].push_back(tmp);
					}
				}
				else{
					scanf("%lld%lld",&cmd[i][1],&cmd[i][2]);
				}
			}
			for(int i=0;i<=n;i++)
				need[i]=0;
			need[n]=1;
			
			for(int i=n;i>=1;i--){
				if(cmd[i][0]==1)
					continue;
				need[cmd[i][1]]+=need[i];
				need[cmd[i][2]]+=need[i];
			}
		
			ll cnt=0,ma=-inf;
			for(int i=1;i<=n;i++){
				if(need[i]){
					for(auto j:v[i])
						mp[j]+=need[i];
					cnt+=need[i]*v[i].size();
				}
			}
			cout<<cnt<<endl;
			for(int i=1;i<=n;i++)
				v[i].clear();
			for(auto i:mp){
				ma=max(i.second,ma);	
			}
			mp.clear();
			if(2*ma<cnt){
				printf("%lld\n",cnt);
			}
			else{
				printf("%lld\n",2*cnt-2*ma);
			}
		} 
	}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值