Codeforces Round #757 div.2 A-D题解

视频讲解:咕了 ,推荐看dls的视频题解

A. Divan and a Store

题目大意

商店里有 n ( 1 ≤ n ≤ 100 ) n(1 \leq n \leq 100) n(1n100) 个不同的巧克力棒,第 i i i 个巧克力棒的价格为 a i a_i ai 美元。Divan只会购买价格在 [ l , r ] [l,r] [l,r] 区间内的巧克力棒,求花费不超过 k k k 美元的情况下,最多可以购买多少巧克力棒。

题解

a i a_i ai 升序排序,从小到大依次选择价格在 [ l , r ] [l,r] [l,r] 范围内的巧克力棒,直到剩余金额不够时停止。

参考代码

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;

const int MAXN=110;
int a[MAXN];

int main()
{
	int T,n,l,r,k,ans,i;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d%d",&n,&l,&r,&k);
		for(i=1;i<=n;i++)
			scanf("%d",&a[i]);
		sort(a+1,a+n+1);
		ans=0;
		for(i=1;i<=n;i++)
		{
			if(a[i]<l)
				continue;
			if(a[i]>r||a[i]>k)
				break;
			k-=a[i];
			ans++;
		}
		printf("%d\n",ans);
	}
}

B. Divan and a New Project

题目大意

计划在一条坐标轴上建造 n + 1 ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) n+1(1 \leq n \leq 2 \cdot 10^5) n+1(1n2105) 个不同的建筑,每个建筑所在的坐标都是整数,且不存在两座建筑位于同一点。
设第 i i i 个建筑的坐标为 x i x_i xi ,从建筑 i i i 到建筑 j j j 需要花费 ∣ x i − x j ∣ |x_i-x_j| xixj 分钟。

建筑编号从 0 0 0 n n n ,有一名商人从第 0 0 0 号建筑物出发,依次访问其他建筑并返回 0 0 0 号建筑物。第 i i i 号建筑将访问 a i a_i ai 次,每次来回花费 2 ⋅ ∣ x 0 − x i ∣ 2\cdot |x_0-x_i| 2x0xi 分钟。

求所有 n + 1 n+1 n+1 座建筑物的坐标,使得花费总时间最少。输出花费总时间,和任意一组合法的解。

题解

等价于求 ∑ i = 1 n 2 ⋅ a i ∣ x i − x 0 ∣ \sum_{i=1}^n{2\cdot a_i|x_i-x_0|} i=1n2aixix0 的最小值。
易得 a i a_i ai 越大,需要 ∣ x i − x 0 ∣ |x_i-x_0| xix0 越小,即离 0 0 0 号建筑越近。 因此按照 a i a_i ai 从大到小,依次放在 x 0 x_0 x0 两侧即可。

参考代码

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;

const int MAXN=200200;
struct Node
{
	int num,id;
}a[MAXN];
int x[MAXN];

bool cmp(Node n1,Node n2)
{
	return n1.num>n2.num;
}

int main()
{
	int T,n,i;
	ll sum;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		for(i=1;i<=n;i++)
		{
			scanf("%d",&a[i].num);
			a[i].id=i;
		}
		sort(a+1,a+n+1,cmp);
		x[0]=0;
		sum=0;
		for(i=1;i<=n;i++)
		{
			if(i&1)
				x[a[i].id]=(i+1)/2;
			else
				x[a[i].id]=-i/2;
			sum+=1ll*(i+1)/2*2*a[i].num;
		}
		printf("%lld\n",sum);
		for(i=0;i<=n;i++)
			printf("%d%c",x[i],i==n?'\n':' ');
	}
}

C. Divan and bitwise operations

题目大意

定义一个非负整数序列的舒适度为其所有子序列上元素的异或之和。
现在有一个长度为 n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) n(1 \leq n \leq 2 \cdot 10^5) n(1n2105) 的非负整数序列 a 1 , a 2 , . . . , a n ( 0 ≤ a i ≤ 2 30 − 1 ) a_1,a_2,...,a_n(0 \leq a_i \leq 2^{30}-1) a1,a2,...,an(0ai2301) ,不知道其具体值,但知道其中 m ( 1 ≤ m ≤ 2 ⋅ 1 0 5 ) m(1 \leq m \leq 2 \cdot 10^5) m(1m2105) 个连续子段上元素的位或值。保证每个元素至少在其中一个子段中出现。
求原序列的舒适度,如果有多组解则输出任意一组即可。答案对 1 0 9 + 7 10^9+7 109+7 取模。

题解

本题可以构造出一组合法的原序列,再用DP求解。不过有更简单的数学方法。
对于二进制下第 d d d 位,设原序列中有 x x x 个元素在该位为 1 1 1 ,剩余 n − x n-x nx 个元素在该位为 0 0 0 。则该位对答案的贡献为
a n s d = 2 n − x ⋅ ∑ k = 0 ⌊ x − 1 2 ⌋ C x 2 k + 1 ans_d= 2^{n-x} \cdot \sum_{k=0}^{\lfloor \frac{x-1}{2} \rfloor}C_{x}^{2k+1} ansd=2nxk=02x1Cx2k+1
由二项式定理易得
( 1 + 1 ) x = C x 0 + C x 1 + . . . + C x x (1+1)^x=C_x^0+C_x^1+...+C_x^x (1+1)x=Cx0+Cx1+...+Cxx

( 1 − 1 ) x = C x 0 − C x 1 + . . . + ( − 1 ) x C x x (1-1)^x=C_x^0-C_x^1+...+(-1)^xC_x^x (11)x=Cx0Cx1+...+(1)xCxx

因此
a n s d = 2 n − x ⋅ ∑ k = 0 ⌊ x − 1 2 ⌋ C x 2 k + 1 = 2 n − x ⋅ ( 1 + 1 ) x − ( 1 − 1 ) x 2 = { 2 n − 1 x > 0 0 x = 0 \begin{aligned} ans_d&= 2^{n-x} \cdot \sum_{k=0}^{\lfloor \frac{x-1}{2} \rfloor}C_{x}^{2k+1}\\ &=2^{n-x}\cdot\frac{(1+1)^x-(1-1)^x}{2}\\ &=\begin{cases} 2^{n-1} &x>0\\ 0 &x=0 \end{cases} \end{aligned} ansd=2nxk=02x1Cx2k+1=2nx2(1+1)x(11)x={2n10x>0x=0

因此统计每一位是否存在 1 1 1 ,若存在则第 d d d 位的贡献为 2 d ⋅ 2 n − 1 2^d \cdot2^{n-1} 2d2n1 ,反之为 0 0 0
实现时,设 y y y 为所有区间位或之和,则答案为
a n s = y ⋅ 2 n − 1 m o d    1 0 9 + 7 ans=y\cdot 2^{n-1} \mod 10^9+7 ans=y2n1mod109+7

参考代码

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;

const ll mod=1e9+7;

ll powmod(ll x,ll p)
{
	ll ret=1;
	while(p)
	{
		if(p&1)
			ret=ret*x%mod;
		x=x*x%mod;
		p>>=1;
	}
	return ret;
}

int main()
{
	int T,n,m,l,r,x;
	ll ans;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		ans=0;
		while(m--)
		{
			scanf("%d%d%d",&l,&r,&x);
			ans|=x;
		}
		ans=ans*powmod(2,n-1)%mod;
		printf("%lld\n",ans);
	}
}

D1+D2. Divan and Kostomuksha

题目大意

给定长度为 n ( 1 ≤ n ≤ 1 0 5 ) n(1 \leq n \leq 10^5) n(1n105) 的正整数序列 a a a ,你需要对其重新排序,使得下式最大:
∑ i = 1 n g c d ( a 1 , a 2 , . . . , a i ) \sum_{i=1}^{n}gcd(a_1,a_2,...,a_i) i=1ngcd(a1,a2,...,ai)

对于 Easy Version, 1 ≤ a i ≤ 5 ⋅ 1 0 6 1 \leq a_i \leq 5 \cdot 10^6 1ai5106
对于 Hard Version, 1 ≤ a i ≤ 2 ⋅ 1 0 7 1 \leq a_i \leq 2 \cdot 10^7 1ai2107

题解

设排序后 g c d ( a 1 , a 2 , . . . , a i ) = g i gcd(a_1,a_2,...,a_i)=g_i gcd(a1,a2,...,ai)=gi ,易得 g i − 1 ∣ g i g_{i-1}|g_i gi1gi ,考虑DP。
c n t x cnt_x cntx 表示是 x x x 倍数的元素个数。
d p x dp_x dpx 表示由 x x x 的倍数的元素组成的子序列的最大值,即
d p x = max ⁡ ∑ x ∣ g i g i dp_x=\max \sum_{x|g_i}g_i dpx=maxxgigi

易得转移式为
d p x = max ⁡ x ∣ y d p y + x ( c n t y − c n t x ) dp_x=\max_{x|y}dp_y+x(cnt_y-cnt_x) dpx=xymaxdpy+x(cntycntx)

对于 Easy Version,可以在 O ( m log ⁡ ( m ) ) O(m\log(m)) O(mlog(m)) 的复杂度内求解 c n t cnt cnt d p dp dp

对于 Hard Version,可以采用以下优化:

  • 求解 c n t cnt cnt 时可以直接枚举每个元素的因数,时间复杂度 O ( n m ) O(n\sqrt{m}) O(nm )
  • 求解 c n t cnt cnt 的同时打表出所有 a i a_i ai 的因子数并集,在求 d p dp dp 时若不在该集合中,则直接跳过计算,时间复杂度约为 O ( n w log ⁡ m ) O(nw\log m) O(nwlogm) ,其中 w w w 为平均因子个数,易得在 a i ≤ 2 ⋅ 1 0 7 a_i \leq 2\cdot 10^7 ai2107 w < 64 w <64 w<64
  • 求解 d p dp dp 时另一种优化方法, x x x 可以只从 y = x ⋅ p y=x\cdot p y=xp 处转移过来,其中 p p p 为素数。DP转移写法类似于素数筛,复杂度可以降低到 O ( m log ⁡ log ⁡ m ) O(m\log \log m) O(mloglogm)

参考代码(Easy Version)

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;

const int MAXN=100100;
const int MAXM=5000500;
int a[MAXN];
ll cnt[MAXM],dp[MAXM];

int main()
{
	int n,i,j,x;
	ll ans;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&x);
		cnt[x]++;
	}
	for(i=1;i<MAXM;i++)
		for(j=i+i;j<MAXM;j+=i)
			cnt[i]+=cnt[j];
	ans=0;
	for(i=MAXM-1;i>=1;i--)
	{
		dp[i]=cnt[i]*i;
		for(j=i+i;j<MAXM;j+=i)
			dp[i]=max(dp[i],dp[j]+i*(cnt[i]-cnt[j]));
		ans=max(ans,dp[i]);
	}
	printf("%lld\n",ans);
}

参考代码(Hard Version)

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;

const int MAXN=100100;
const int MAXM=20002000;
bool vis[MAXM];
ll cnt[MAXM],dp[MAXM];

int main()
{
	int n,i,j,x;
	ll ans;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&x);
		for(j=1;j*j<=x;j++)
		{
			if(x%j==0)
			{
				cnt[j]++;
				cnt[x/j]++;
				if(j*j==x)
					cnt[j]--;
				vis[j]=vis[x/j]=1;
			}
		}
	}
	ans=0;
	for(i=MAXM-1;i>=1;i--)
	{
		if(!vis[i])
			continue;
		dp[i]=cnt[i]*i;
		for(j=i+i;j<MAXM;j+=i)
		{
			if(!vis[j])
				continue;
			dp[i]=max(dp[i],dp[j]+i*(cnt[i]-cnt[j]));
		}
		ans=max(ans,dp[i]);
	}
	printf("%lld\n",ans);
}

E. Divan and a Cottage

题目大意

有一个屋子,屋内温度会随着屋外温度而变化。若某天早上屋内温度为 P P P ,屋外温度为 T T T ,则第二天屋内温度 P n e w P_{new} Pnew 的变化规律为:

  • P < T P<T P<T ,则 P n e w = P + 1 P_{new}=P+1 Pnew=P+1 ;
  • P > T P>T P>T ,则 P n e w = P − 1 P_{new}=P-1 Pnew=P1 ;
  • P = T P=T P=T ,则 P n e w = P P_{new}=P Pnew=P ;

n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) n(1 \leq n \leq 2 \cdot 10^5) n(1n2105) 天,第 i i i 天的屋外温度为 T i ( 0 ≤ T i ≤ 1 0 9 ) T_i(0 \leq T_i \leq 10^9) Ti(0Ti109) ,并且有 k i ( ∑ k i ≤ 2 ⋅ 1 0 5 ) k_i(\sum k_i \leq 2 \cdot 10^5) ki(ki2105) 个询问,每个询问给定一个 x ( 0 ≤ x ≤ 1 0 9 ) x(0 \leq x \leq 10^9) x(0x109) ,表示若第 1 1 1 天屋内温度为 x x x ,求第 i i i 天屋内温度为多少。
询问 x x x 强制在线。

题解

动态开点线段树
TBD

参考代码


  • 12
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值