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;
}
