Codeforces Round #673 Div. 2 Sep/27/2020 23:05UTC+8
比赛链接:https://codeforces.com/contest/1417
比赛记录:https://blog.csdn.net/cheng__yu_/article/details/105395197
C. k-Amazing Numbers
题意:给定一个长度为 n n n 的序列,问在所有 k k k 子段都出现的最小的数字是多少? k ∈ [ 1 , n ] , 1 ≤ n ≤ 3 × 1 0 5 k\in[1,n],1\le n \le 3\times 10^5 k∈[1,n],1≤n≤3×105
思路:
- 可以贪心地想,从最小的数字开始,它能够包含的最小区间是多大,那么后面更大的区间也是能够包含的。比如,n = 10 ,1 出现在2、3、6的位置上,那么 1 能够包含的最小区间就是 5 。因此后面的 5 - 10 的答案都是 1 。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=3e5+10;
int t,n;
int a[maxn],ans[maxn];
vector<int> vec[maxn];
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=0; i<=n; ++i) vec[i].clear(),ans[i]=-1;
for(int i=1; i<=n; ++i)
{
vec[i].push_back(0);
vec[i].push_back(n+1);
}
for(int i=1; i<=n; ++i)
{
int x;
scanf("%d",&x);
vec[x].push_back(i);
}
int tail=n;
for(int i=1; i<=n; ++i)
{
if(vec[i].size()==2) continue;
sort(vec[i].begin(),vec[i].end());
int dis=0;
for(int j=1; j<vec[i].size(); ++j)
dis=max(dis,vec[i][j]-vec[i][j-1]);
for(int j=dis; j<=tail; ++j)
ans[j]=i;
tail=min(tail,dis-1);
}
for(int i=1; i<=n; ++i)
printf("%d%c",ans[i],i==n?'\n':' ');
}
return 0;
}
D. Make Them Equal
题意:给定一个长度为 n 的数组,每次可以选择三个数 i 、 j 、 x i 、j 、x i、j、x ( 1 ≤ i , j ≤ n , 0 ≤ x , ≤ 1 0 9 ) (1\le i,j\le n ,0\le x,\le 10^9) (1≤i,j≤n,0≤x,≤109),使得 a i = a i − x i , a j = a j + x i a_i=a_i-xi,a_j=a_j+xi ai=ai−xi,aj=aj+xi。问在 3n 次操作内,能不能让所有数都相等,输出方案。在每次操作后要保持任意 a i a_i ai 始终为非负数 ( 1 ≤ a I ≤ 1 0 5 , 1 ≤ n ≤ 1 0 4 ) (1\le a_I \le 10^5,1\le n\le 10^4) (1≤aI≤105,1≤n≤104)
思路:
- 每个 a i a_i ai 的目标就是达到平均数 avg,那么就可以把 a i a_i ai 大于 avg 的部分向 a 1 a_1 a1 转移,即 a i a_i ai 对 i i i 取模
- 转移后我们发现这个 a i a_i ai 可能会大于 avg ,此时 − i - i −i 是行不通了(会变负数),那么就可以从 a 1 a_1 a1 位置补充过来,变成 i i i 之后,然后在减 i i i ,即再向 a 1 a_1 a1 转移。(向 a 1 a_1 a1 转移的操作可以合在一起)
- 最后可以得到,除了 a 1 a_1 a1 之外其余都为 0 0 0 。那么就从 a 1 a_1 a1 转移到其余位置就好了。
- 总结一下:首先从 a 1 a_1 a1 转移到 a i a_i ai,让 a i a_i ai 变成 i i i 的倍数,然后 a i a_i ai 向 a 1 a_1 a1 转移,最后从 a 1 a_1 a1 转移到 a i a_i ai,恰好在 3n 次之内完成
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
int t,n;
int a[maxn];
struct Node
{
int i,j,x;
};
vector<Node> ans;
int main()
{
scanf("%d",&t);
while(t--)
{
ans.clear();
scanf("%d",&n);
ll sum=0;
for(int i=1; i<=n; ++i) scanf("%d",&a[i]),sum+=a[i];
if(sum%n!=0)
{
puts("-1");
continue;
}
ll avg=sum/n;
for(int i=2; i<=n; ++i)
{
if(a[i]%i!=0)
{
int need=i-a[i]%i;
a[i]+=need;
ans.push_back({1,i,need});
}
ans.push_back({i,1,a[i]/i});
}
for(int i=2; i<=n; ++i)
ans.push_back({1,i,avg});
printf("%d\n",ans.size());
for(auto u: ans)
printf("%d %d %d\n",u.i,u.j,u.x);
}
return 0;
}
E. XOR Inverse
题意:给定一个包含 n n n 个非负整数的数组,你可以选择一个非负整数 x x x ,对每一个 a i a_i ai 做异或操作,得到 b i b_i bi ,即, b i = a i ⊕ x b_i=a_i\oplus x bi=ai⊕x。问取怎样的 x 能够使得 b 的逆序对最少,如果有多个 x 满足条件,则取最小的 x 。输出最少的逆序对,和最小的 x 。
思路:
- 按位贪心,对每一层求逆序对,然后把当前层为 0 的放在一起,1 放在一起,继续往下贪心即可
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
int n;
vector<int> a;
ll inv[32][2];
void solve(vector<int> &a,int depth)
{
if(depth<0) return;
if(a.size()==0) return;
vector<int> left,right;
ll cnt0=0,cnt1=0,sum01=0,sum10=0;
for(int i=0; i<a.size(); ++i)
{
if(a[i]>>depth&1)
{
cnt1++;
sum01+=cnt0;
right.push_back(a[i]);
}
else
{
cnt0++;
sum10+=cnt1;
left.push_back(a[i]);
}
}
inv[depth][0]+=sum01;
inv[depth][1]+=sum10;
solve(left,depth-1);
solve(right,depth-1);
}
int main()
{
scanf("%d",&n);
a.resize(n);
for(auto &x: a) cin>>x;
solve(a,31);
ll ans=0,x=0;
for(int i=0; i<=31; ++i)
{
if(inv[i][0]>=inv[i][1]) ans+=inv[i][1];
else ans+=inv[i][0],x|=(1<<i);
}
printf("%lld %lld\n",ans,x);
return 0;
}