A. Divan and a Store
题目大意
商店里有 n ( 1 ≤ n ≤ 100 ) n(1 \leq n \leq 100) n(1≤n≤100) 个不同的巧克力棒,第 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(1≤n≤2⋅105) 个不同的建筑,每个建筑所在的坐标都是整数,且不存在两座建筑位于同一点。
设第
i
i
i 个建筑的坐标为
x
i
x_i
xi ,从建筑
i
i
i 到建筑
j
j
j 需要花费
∣
x
i
−
x
j
∣
|x_i-x_j|
∣xi−xj∣ 分钟。
建筑编号从 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| 2⋅∣x0−xi∣ 分钟。
求所有 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=1n2⋅ai∣xi−x0∣ 的最小值。
易得
a
i
a_i
ai 越大,需要
∣
x
i
−
x
0
∣
|x_i-x_0|
∣xi−x0∣ 越小,即离
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(1≤n≤2⋅105) 的非负整数序列
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(0≤ai≤230−1) ,不知道其具体值,但知道其中
m
(
1
≤
m
≤
2
⋅
1
0
5
)
m(1 \leq m \leq 2 \cdot 10^5)
m(1≤m≤2⋅105) 个连续子段上元素的位或值。保证每个元素至少在其中一个子段中出现。
求原序列的舒适度,如果有多组解则输出任意一组即可。答案对
1
0
9
+
7
10^9+7
109+7 取模。
题解
本题可以构造出一组合法的原序列,再用DP求解。不过有更简单的数学方法。
对于二进制下第
d
d
d 位,设原序列中有
x
x
x 个元素在该位为
1
1
1 ,剩余
n
−
x
n-x
n−x 个元素在该位为
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=2n−x⋅k=0∑⌊2x−1⌋Cx2k+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 (1−1)x=Cx0−Cx1+...+(−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=2n−x⋅k=0∑⌊2x−1⌋Cx2k+1=2n−x⋅2(1+1)x−(1−1)x={2n−10x>0x=0
因此统计每一位是否存在
1
1
1 ,若存在则第
d
d
d 位的贡献为
2
d
⋅
2
n
−
1
2^d \cdot2^{n-1}
2d⋅2n−1 ,反之为
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=y⋅2n−1mod109+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(1≤n≤105) 的正整数序列
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=1∑ngcd(a1,a2,...,ai)
对于 Easy Version,
1
≤
a
i
≤
5
⋅
1
0
6
1 \leq a_i \leq 5 \cdot 10^6
1≤ai≤5⋅106 。
对于 Hard Version,
1
≤
a
i
≤
2
⋅
1
0
7
1 \leq a_i \leq 2 \cdot 10^7
1≤ai≤2⋅107 。
题解
设排序后
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
gi−1∣gi ,考虑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=maxx∣gi∑gi
易得转移式为
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=x∣ymaxdpy+x(cnty−cntx)
对于 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 ai≤2⋅107 时 w < 64 w <64 w<64 。
- 求解 d p dp dp 时另一种优化方法, x x x 可以只从 y = x ⋅ p y=x\cdot p y=x⋅p 处转移过来,其中 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=P−1 ;
- 若 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(1≤n≤2⋅105) 天,第
i
i
i 天的屋外温度为
T
i
(
0
≤
T
i
≤
1
0
9
)
T_i(0 \leq T_i \leq 10^9)
Ti(0≤Ti≤109) ,并且有
k
i
(
∑
k
i
≤
2
⋅
1
0
5
)
k_i(\sum k_i \leq 2 \cdot 10^5)
ki(∑ki≤2⋅105) 个询问,每个询问给定一个
x
(
0
≤
x
≤
1
0
9
)
x(0 \leq x \leq 10^9)
x(0≤x≤109) ,表示若第
1
1
1 天屋内温度为
x
x
x ,求第
i
i
i 天屋内温度为多少。
询问
x
x
x 强制在线。
题解
动态开点线段树
TBD
参考代码