Codeforces Round #757 (Div. 2)
A. Divan and a Store
题意:用\(k\)元钱最多可以购买多少件价格在\([l,r]\)的物品。
贪心,排序后按照在\([l,r]\)范围内价格从小到大的顺序取即可。
/* Author: EndlessK
* Time: 2021-11-26 19:15:07
**/
#include<bits/stdc++.h>
#define pb push_back
#define ll long long
#define fast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int maxn = 110;
ll a[maxn];
int n;
int main()
{
fast;
int T;
cin>>T;
while(T--)
{
int l,r,k;
cin>>n>>l>>r>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a+1,a+n+1);
ll sum=0;
ll ans=0;
for(int i=1;i<=n;i++)
{
if(a[i]>=l && a[i]<=r)
{
if(sum+a[i]<=k) sum+=a[i],ans++;
}
}
cout<<ans<<'\n';
}
return 0;
}
B. Divan and a New Project
题意:给出\(n\)个系数,在数轴上找\(n+1\)个点,使得\(\sum\limits_{i=1}^n2\cdot a_i \cdot |x_0-x_i|\)最小。
贪心,假定\(x_0\)处于\(0\),先将点按照系数从大到小排序,系数大的应当离的更近,顺序按照\(1\rightarrow-1\rightarrow2\rightarrow-2\)的顺序进行分配即可。
/* Author: EndlessK
* Time: 2021-11-26 19:15:07
**/
#include<bits/stdc++.h>
#define pb push_back
#define ll long long
#define fast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int maxn = 200010;
int n;
ll b[maxn];
struct node{
ll x;
int idx;
} a[maxn];
bool cmp(node a,node b)
{
return a.x>b.x;
}
int main()
{
fast;
int T;
cin>>T;
while(T--)
{
ll tmp=1;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].x;
a[i].idx=i;
}
sort(a+1,a+n+1,cmp);
b[0]=0;
ll ans=0;
for(int i=1;i<=n;i++)
{
b[a[i].idx]=tmp;
ans+=abs(tmp)*a[i].x;
if(tmp>0) tmp=-tmp;
else tmp=-tmp+1;
}
cout<<2*ans<<'\n';
for(int i=0;i<=n;i++)
{
cout<<b[i]<<' ';
}
cout<<'\n';
}
return 0;
}
C. Divan and bitwise operations
题意:\(n\)个数,给定\(m\)个区间的同或和,求出整个序列的\(coziness\)。
赛后看了思路懂了整个推导过程。
首先对于每一个序列而言,如果仅存在\(x\)和\(0\),那么只要\(x\)的个数不为\(0\),其\(coziness\)是不变的。
那么对于每一个二进制数位,其实我只需要知道这一位是否有\(1\)就可以知道答案。
同或和还有一个性质:两个区间的同或和进行或运算可以得到他们并集的同或和。根据这一点,加上题目中提到必然会遍及每一个元素,那么把给出的\(m\)个区间的同或和进行或运算即可得到\([1,n]\)的同或和\(x\)。
这样我们就可以假设\(x\)恰为第一个数(虽然不符合题目但不影响结果),这样结果就为\(x\cdot 2^{n-1}\),最后记得取模。
/* Author: EndlessK
* Time: 2021-11-26 19:15:07
**/
#include <bits/stdc++.h>
#define pb push_back
#define ll long long
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
using namespace std;
const int maxn = 200010;
const int mod = 1000000007;
ll Pow(ll a, ll b)
{
if (b == 0)
return 1;
if (b % 2)
return a * Pow(a, b - 1) % mod;
ll tmp = Pow(a, b / 2);
return tmp * tmp % mod;
}
int main()
{
fast;
int T;
cin >> T;
while (T--)
{
int n, m;
cin >> n >> m;
ll l, r, x;
ll sum = 0;
for (int i = 1; i <= m; i++)
{
cin >> l >> r >> x;
sum = (sum | x);
}
ll ans = 1;
ans = Pow(2, n - 1) * sum % mod;
cout << ans << '\n';
}
return 0;
}
D. Divan and Kostomuksha
题意:给出\(n\)个数,重新排列这些数,使得他们的\(gcd\)前缀和(我自己想的)最大。
赛时想了个类似埃筛的做法,WA6之后火速叉掉了自己的做法。
首先我们用\(cnt[i]\)记录有多少个数存在因子\(i\),为了节约时间,初始仅将\(cnt[a[i]]=1\),然后进行\(cnt\)的状态转移,基本思想为\(cnt[i]+=cnt[i\cdot j]\)(但并不是最终解法)。
随后我们用\(f[i]\)表示最大值为\(i\)时除去\(i\)数字的其他数字的最大贡献,也可以很快地给出状态转移方程\(f[i\cdot j]=max(f[i\cdot j], f[i]+i\cdot (cnt[i]-cnt[i*j]))\)。最后取\(f[i]+cnt[i]\cdot i\)的最大值即为答案。
但事实是效率并不高,因为在状态转移中,由于合数的存在,其实有很多重复的状态转移,并且该状态转移一定不是最优的,那么我们可以将上述状态转移方程中的\(j\)全部替换为质数,这样可以减少状态转移的次数,从而提高效率。这样,只需要预处理进行质数筛即可。
/* Author: EndlessK
* Time: 2021-11-26 19:15:07
**/
#include<bits/stdc++.h>
#define pb push_back
#define ll long long
#define fast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int maxn = 2e7+10;
int n;
ll f[maxn]={0};
ll cnt[maxn]={0};
ll a[100010];
bool vis[maxn]={0};
ll prime[2000010];
int pos=0;
void init()
{
for(int i=2;i<=20000000;i++)
{
if(!vis[i])
{
prime[++pos]=i;
}
for(int j=1;j<=pos && prime[j]*i<=20000000;j++)
{
vis[prime[j]*i]=1;
if(i%prime[j]==0) break;
}
}
}
int main()
{
fast;
init();
cin>>n;
ll g=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
g=max(g,a[i]);
}
for(int i=1;i<=n;i++)
{
cnt[a[i]]++;
}
for(int i=1;i<=pos;i++)
{
for(int j=20000000/prime[i];j>=0;j--)
{
cnt[j]+=cnt[j*prime[i]];
}
}
ll ans=0;
for(int i=1;i<=g;i++)
{
ans=max(ans,f[i]+cnt[i]*i);
for(int j=1;j<=pos;j++)
{
if(i*prime[j]>20000000) break;
f[i*prime[j]]=max(f[i*prime[j]],f[i]+i*(cnt[i]-cnt[i*prime[j]]));
}
}
cout<<ans<<'\n';
return 0;
}