ICPC NEAU Programming Contest 2020

B.数字分布 (构造)

题目描述
https://nanti.jisuanke.com/t/45507
构造一个含有n个正整数的序列,并满足以下要求:
1.序列是不递减的,a1 <= a2 <= … <= an
2.序列中满足 j<i 且 ai mod aj = 0 的对数为m
3.序列中所有数字不大于2⋅n

Solution

首先我们考虑1:
1可以和任何数组成一对。所以对于当前位置放置1,可以组成( n - i )对。
再考虑非1的情况:
为了防止它们成对,我们对这些位置填的数在(n + 1)到 2 * n 之间。
这样它们就被分成一个个连续相同数字的块,组对只能是在相同的块里组,假设当前块有k个,组成的对有k *(k - 1)/ 2对.

按上述方法构造即可。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int SZ = 2e5 + 20; 
int n;
vector<int>num;
ll m;

int main()
{
	int T;
	scanf("%d",&T);
	while(T --)
	{
		scanf("%d%lld",&n,&m);
		num.clear();
		int i = 1;
		ll w = n;
		while(i <= n && m - n + i >= 0)
		{
			m -= n - i;
			i ++;
			printf("1 ");
		}
		while(w && m)
		{
			while(w * (w - 1) / 2 <= m)
			{
				num.push_back(w);
				m -= (w - 1) * w / 2; 
			}
			w --;
		}
		w = n + 1;
		int j = 0;
		for(;i <= n;i ++)
		{
			printf("%d ",w);
			if(j < num.size())
			{
				num[j] --;
				if(num[j] == 0) 
				{
					j ++;
					w ++;
				}
			} 
			else 
				w++;
		}
		printf("\n");
	}
	return 0;	
}

D.旅游(倍增)

https://blog.csdn.net/qq_45620330/article/details/106647957

G.选根(dp)

和这题非常像
https://www.luogu.com.cn/problem/P3478

题目描述

https://nanti.jisuanke.com/t/45572
有一颗有n个结点树,结点被编号为1~n,记根结点深度为1,如果第i个结点的深度是d,则它贡献的价值是d×wi,这棵树的价值是所有结点的价值和。
求当根结点为1~n时,树的价值分别为多少?

Solution

换根dp经典题

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int SZ = 2e6 + 20;
int n,temp;
ll dp[SZ],dep[SZ];
int fist[SZ];
ll w[SZ],ans[SZ],sumw[SZ];
struct zt
{
	int v,nxt;
}line[SZ];

inline void build(int x,int y)
{
	line[++ temp] = (zt){y,fist[x]};
	fist[x] = temp;
}

inline void dfs1(int u,int far)
{
	sumw[u] = w[u]; 
	dp[u] = dep[u] * w[u];
	for(int i = fist[u];i != -1;i = line[i].nxt)
	{
		int v = line[i].v;
		if(v == far) continue;
		dep[v] = dep[u] + 1;
		dfs1(v,u);
		sumw[u] += sumw[v];
		dp[u] += dp[v];
	}	
}

inline void dfs2(int u,int far)
{
	ans[u] = dp[u];
	for(int i = fist[u];i != -1;i = line[i].nxt)
	{
		int v = line[i].v;
		if(v == far) continue;
		dp[v] = dp[u] + sumw[u] - 2 * sumw[v]; 
		sumw[u] -= sumw[v];
		sumw[v] += sumw[u];
		dfs2(v,u);	
		sumw[v] -= sumw[u];
		sumw[u] += sumw[v];
	}	
}

int main()
{
	int a,b;
	int T;	
	scanf("%d",&T);
	while(T --)
	{
		temp = 0;
		memset(fist,-1,sizeof(fist));
		scanf("%d",&n);
		for(int i = 1;i <= n;i ++) scanf("%lld",&w[i]);
		for(int i = 1;i < n;i ++)
		{
			scanf("%d%d",&a,&b);
			build(a,b);
			build(b,a);
		}	
		dep[1] = 1;
		dfs1(1,1);
		dfs2(1,1);
		for(int i = 1;i <= n;i ++)
			printf("%lld ",ans[i]);
		printf("\n");
	}
	return 0;
}

H.排序

题目描述

https://nanti.jisuanke.com/t/45513
给你两个长度均为n的数列ai和bi定义函数
f(l,r)= ∑ai×bi (l <= i <= r)
​g(x)= ∑∑f(l,r) (1 <= l <= x) (1 <= r <= x)
你可以改变ai内各元素的顺序,使g(n)最小,求这个最小值。由于答案很大,请输出答案对10^9+7 取余后的值.

Solution

因为bi不可以换序,所以对于每个bi来说,它要被计算i * (n - i + 1)次,
ai可以换序,所以只要ai中大的数与bi * i * (n - i + 1)中小的数相乘,使得乘积和最小即可,具体操作只要把ai 和 bi * i * (n - i + 1)按照反方向排序,对应相乘即可。

注意取模,别炸了LL。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//__builtin_popcount(n);
//-std=c++11
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define RI register int
const int MOD = 1e9 + 7;
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int SZ = 2e5 + 10;
int n;
ll a[SZ],b[SZ];
bool cmp(ll x,ll y)
{
	return x > y;
}
ll c[SZ];
int main()
{
	int T;
	scanf("%d",&T);
	while(T --)
	{
		ll ans = 0;
		scanf("%d",&n);
		for(int i = 1;i <= n;i ++) scanf("%lld",&a[i]);
		for(int i = 1;i <= n;i ++) scanf("%lld",&b[i]);
		for(int i = 1;i <= n;i ++) b[i] = b[i] * i * (n - i + 1);
		sort(a + 1,a + n + 1,cmp);
		sort(b + 1,b + n + 1);
		for(int i = 1;i <= n;i ++) 
		{
			c[i] = (a[i] * (b[i] % MOD)) % MOD;
			ans = (ans + c[i]) % MOD; 
		} 
		printf("%lld\n",ans);
	}
	return 0;
} 

I.支付(构造)

https://nanti.jisuanke.com/t/45487

题目描述

有n个物品,第ii个物品的价值为wi,选择若干物品,这些物品的价值和为S
作为支付手段,希望选择一部分物品能精确地支付不大于k的所有金额,即任意s满足1≤s≤k都有对应的选择方案使S=s,给定k的值,构造满足要求的序列wi,使n尽可能小的前提下wi的和尽可能小。

Solution

构造一个20,21,22,…,2t 和 k - Σ2i的序列为答案。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll k;
int main()
{
	int T;
	scanf("%d",&T);
	while(T --)
	{
		scanf("%lld",&k);
		ll temp = k,cnt = 0;
		while(temp){cnt ++;temp >>= 1;}
		ll sum = pow(2,cnt - 1);
		k -= (sum - 1);
		ll t = 1; 
		printf("%lld\n",cnt);
		for(int i = 1;i <= cnt - 1;i ++)
		{
			printf("%lld ",t);
			t <<= 1;
		}
		printf("%lld\n",k);
	}
	return 0;
}

J.球衣

https://nanti.jisuanke.com/t/45578

题目描述

共有n支队伍参加足球联赛,每支队伍有3件球衣编号为1∼3,每件球衣都有颜色.球衣颜色按RGB格式给出,RGB颜色表示分为3部分,分别是红色绿色蓝色的亮度。每种颜色的亮度在0∼255之间,用十六进制表示,如红色亮度为255,绿色亮度为120,蓝色亮度为160就组成了粉色,RGB十六进制表示为#FF78A0,每种颜色的亮度占2位
在比赛时,为了避免混淆,两队的球衣颜色不能过于相似,要求两队球衣的颜色差不小于128,颜色差是两队球衣颜色红绿蓝亮度差的绝对值之和,#FF78A0 rgb(255, 120, 160)和#78A0FF rgb(120,160,255)的颜色差是∣255−120∣+∣120−160∣+∣160−255∣=270
在A队主场对阵B队的比赛中,A队首先选择1号球衣,B队依次选择1∼3号球衣,如果都不能满足要求,A队选择2号球衣,B队依次选择1∼3号球衣,如果还不满足,A队选择3号球衣,B队依次选择1∼3号球衣
也就是说两队都希望选择编号尽可能小的球衣,但是主场作战的队伍优先选择,如果主场队伍编号小的球衣与客场队伍的三件球衣都相似,才会选择编号大的球衣.
比赛采取主客场双循环赛制,请你计算每次比赛两队的球衣颜色

Solution

十六进制读入,按要求模拟即可。

代码

#include <bits/stdc++.h>
using namespace std;
const int SZ = 100 + 20;
int n;
int color[SZ][5];
int contest[2][SZ][SZ];
int dif(int x,int y)
{
	int sum = 0;
	for(int i = 0;i < 3;i ++)
	{
		sum += abs(x % 256 - y % 256);
		x /= 256;
		y /= 256;
	}
	return sum;
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T --)
	{
		memset(contest,0,sizeof(contest));
		scanf("%d",&n);
		for(int i = 1;i <= n;i ++)
			for(int j = 1;j <= 3;j ++)
				scanf("%X",&color[i][j]);
		for(int i = 1;i <= n;i ++)
			for(int j = 1;j <= n;j ++)
			{
				if(i == j) continue;
				for(int t = 3;t >= 1;t --)
					for(int p = 3;p >= 1;p --)
						if(dif(color[i][t],color[j][p]) >= 128)
						{
							contest[0][i][j] = t;
							contest[1][j][i] = p;
						}
			} 
			for(int k = 0;k <= 1;k ++)
				for(int i = 1;i <= n;i ++)
					for(int j = 1;j <= n;j ++) 
						printf("%d%c",contest[k][i][j],j == n?'\n':' ');
	}
	return 0;
}

M.再来异或

https://nanti.jisuanke.com/t/45576

题目描述

给你具有n个结点n−1条边的无向无环连通图,结点编号1∼n,每条边上有一个数作为他的边权,定义函数f(i,j)为连接i,j的简单路径的所有边权的异或值
求⨁⨁f(i,j) (1 <= i <= n) (i <= j <= n)
⊕为按位异或运算,⨁i ( l <= i <= r)表示l∼r所有整数异或后的结果

Solution

求树中所有结点两两组合的函数值的异或和,只需要考虑每条边被计算了多少次,偶数次则为0,奇数次则对答案有贡献。只要判断当前边两侧节点个数的乘积为奇数还是偶数即可判断当前边被计算的奇偶次。

代码

#include <bits/stdc++.h>
using namespace std;
const int SZ = 2e5 + 10;
typedef long long ll;
int ans,n;
struct zt
{
	int v,w,nxt;
}line[SZ << 1];
int temp,fist[SZ];
inline void build(int x,int y,int z)
{
	line[++ temp] = (zt){y,z,fist[x]};
	fist[x] = temp; 
} 
inline int dfs(int u,int far)
{
	int cnt = 1;
	for(int i = fist[u];i != -1;i = line[i].nxt)
	{
		int v = line[i].v;
		if(v == far) continue;
		int temp = dfs(v,u);
		if(temp % 2 * (n - temp) % 2 == 1)
			ans ^= line[i].w;
		cnt += temp; 
	}
	return cnt;
}
int main()
{ 
	int T;
	scanf("%d",&T);
	while(T --)
	{
		temp = 0;
		memset(fist,-1,sizeof(fist));
		scanf("%d",&n);
		int a,b,c;
		for(int i = 1;i <= n - 1;i ++)
		{
			scanf("%d%d%d",&a,&b,&c);
			build(a,b,c);
			build(b,a,c);
		}
		ans = 0;
		dfs(1,1);
		printf("%d\n",ans); 
	} 
	return 0;
}

2020.6.9

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值