CF 做题记录
前言
Codeforces Round 836 (Div. 2)
C. Almost All Multiples
这题挺妙的。
很容易发现 $ n% x==0$ 时无解。
让 \(p[x]=n,p[1]=x,p[n]=1\) 就是一个可行解。
但此题要求字典序最小,我们以 \(8,2\) 为例。
现在的序列:
2 8 3 4 5 6 7 1
字典序最小的序列:
2 4 3 8 5 6 7 1
可以发现每次都把 \(n\) 向后移,而且这个可以交换的位置构成了一个链:\(x,x_1,x_2 \cdots n\),这个链的所有值满足 \(p[x_i] \mod x_{i-1} = 0,p[x_{i-1}] \mod x_i=0\)。
按照上面的判定条件跑一遍即可。
code。
D. Range = √Sum
我们先看当 \(n\) 为偶数的情况。
设 \(mid=n/2\),可以构造出 \(mid,mid+1,\cdots n-1,n+1,n+1,\cdots n+mid\)。
这里很好证明。
\(max-min=n\)。
\(sum=\dfrac{(mid+mid+n)\times (n+1)}{2}-n=n^2\)。
\(sqrt{sum}=max-min=n\)。
那把它推展到奇数。
观察一下 \(4\) 构成的序列:
2 3 5 6
想办法变成 \(3\) 个数,最好操作的就是把 \(3\) 去掉,其余的加一。变成:
3 6 7
满足条件。
推广到整个正奇数集,先构造出 \(n+1\) 的序列,然后把 \(n\) 去掉,其余的加一。
证明:
因为 \(n\) 一定被包含于 \(n+1\) 的序列中,可以去掉 \(n\)。
为了保持总和不变,剩下的 \(n\) 个数都加一。
由于 \(n+1\) 的序列中的数各不相同,都加一后也一定不同,保证了 \(n\) 的序列的不重性。
code。
E. Tick, Tock
待补。
F. Decent Division
待补。
Codeforces Round 840 (Div. 2)
C.Another Array Problem
可以发现:
-
\(n=2\),\(ans=max(a[1] + a[2], 2 * abs(a[1] - a[2]))\)。
-
\(n=3\),\(ans=max(1ll * a[1] + 1ll * a[2] + 1ll * a[3], max(1ll * 3 * a[1], max(1ll * 3 * abs(a[2] - a[3]), max(1ll * 3 * abs(a[1] - a[2]), 1ll * a[3] * n))))\)
-
\(n>3\),\(ans=maxx*n\)。
因为只要对一个区间连续两次操作就可以变成 \(0\),\(n>3\) 时无论怎么变都不如把所有数变成最大值(有点不好发现)。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, maxx;
int a[N];
void solve()
{
scanf("%d", &n);
maxx = 0;
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
maxx = max(maxx, a[i]);
}
if (n >= 4)
return printf("%lld\n", 1ll * n * maxx), void();
if (n == 2)
return printf("%d\n", max(a[1] + a[2], 2 * abs(a[1] - a[2]))), void();
if (n == 3)
return printf("%lld\n", max(1ll * a[1] + 1ll * a[2] + 1ll * a[3], max(1ll * 3 * a[1], max(1ll * 3 * abs(a[2] - a[3]), max(1ll * 3 * abs(a[1] - a[2]), 1ll * a[3] * n))))), void();
printf("%d\n", a[1]);
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
solve();
}
Codeforces Round 948 (Div. 2)
B. Binary Colouring
卡我一个小时。
先二进制拆分,求出 \(01\) 序列。
考虑有相邻的如 \(14\)。
\(0,1,1,1\)
可以知道:\(2^x+2^{x+1}=2^{x+2}-2^{x}\)。
那上序列可改为:\(0,-1,0,2\)。
有知道 \(2\times 2^x=2^{x+1}\)。
序列又可改为 \(0,-1,0,0,1\)。
此题解决。
#include<bits/stdc++.h>
using namespace std;
int n;
int a[40];
void solve()
{
scanf("%d",&n);
memset(a,0,sizeof a);
int now=n,m=0;
for(int i=31;i>=0;i--)
{
if(now-(1<<i)>=0)
{
now-=(1<<i);
a[i]=1;
m=max(m,i);
}
}
for(int i=0;i<=m;i++)
{
if(a[i]==2) a[i]=0,a[i+1]++;
if(a[i]==1&&a[i+1]==1)
{
a[i]=-1,a[i+1]=0,a[i+2]++;
}
}
if(a[m+1]) m++;
printf("%d\n",m+1);
for(int i=0;i<=m;i++)
printf("%d ",a[i]);
puts("");
}
int main()
{
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}
C. Nikita and LCM
先排序,计算所有值的 lcm 记为 \(ss\),当 \(ss \neq a[n]\),或再计算时就大于 \(10^9\),答案为 \(n\)。
由于 \(ss\) 一定小于 \(10^9\),可以枚举 \(ss\) 的因数作为序列的 lcm,所有它的因数加进来只会使最小公倍数大,且不会超过枚举的最小公倍数。
最后判断是否与枚举 lcm 的相同,记录答案即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2020;
int n, a[N], ans;
map<long long, int> vis;
int gcd(int x, int y)
{
if (!y)
return x;
return gcd(y, x % y);
}
void check(int x)
{
int res = 1, cnt = 0;
for (int i = 1; i <= n; i++)
{
if (x % a[i] == 0)
{
int d = gcd(res, a[i]);
res = res / d * a[i];
cnt++;
}
}
if (res == x)
ans = max(cnt, ans);
}
void solve()
{
scanf("%lld", &n);
vis.clear(), ans = 0;
int ss = 1;
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; i++)
{
vis[a[i]] = 1;
int d = gcd(ss, a[i]);
ss = ss / d;
ss *= a[i];
if (ss > 1e9)
{
printf("%lld\n", n);
return;
}
}
if (ss != a[n])
{
printf("%lld\n", n);
return;
}
for (int i = 1; i * i <= ss; i++)
{
if (ss % i == 0)
{
if (!vis[i])
check(i);
if (!vis[ss / i])
check(ss / i);
}
}
printf("%lld\n", ans);
}
signed main()
{
int T;
scanf("%lld", &T);
while (T--)
solve();
return 0;
}
D. XORificator
很有意思的一道题。
Codeforces Round 939 (Div. 2)
D. Nene and the Mex Operator
有意思的题。
本题的 trick 是每一个 \([l,r]\) 的区间都可以让这个区间的每一个数变为:\(r-l+1\)。
由于此题 \(n\) 很小,可以暴力枚举 \(2^n\) 种情况,哪些要使用 mex 操作,求出最大值和要操作的区间。
对于每一个区间。试着手动操作,可以发现 \([l,r-1]\) 完成可以使 \([l,r]\) 完成。
这里递归求解操作即可。
#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
int n;
int a[20],vis[20];
vector<pii>way;
void domex(int l,int r)
{
memset(vis,0,sizeof vis);
for(int i=l;i<=r;i++) vis[a[i]]++;
int mex=0;
while(vis[mex]) mex++;
for(int i=l;i<=r;i++) a[i]=mex;
way.push_back({l,r});
}
void solve(int l,int r)
{
if(l==r)
{
if(a[l]) domex(l,r);
return ;
}
solve(l,r-1);
if(a[r]!=r-l)
domex(l,r),solve(l,r-1);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int fi=(1<<n)-1,ans=0,cur;
for(int i=0;i<=fi;i++)
{
int l=1,res=0;
while(l<=n)
{
int r=l;
if(i&(1<<l-1))
{
while(r<=n&&i&(1<<r-1)) r++;
res+=(r-l)*(r-l);
}
else res+=a[l],r=l+1;
l=r;
}
if(res>ans) ans=res,cur=i;
}
//cout<<cur<<endl;
int l=1;
while(l<=n)
{
int r=l;
if(cur&(1<<l-1))
{
while(r<=n&&cur&(1<<r-1)) r++;
solve(l,r-1);
domex(l,r-1);
}
else r=l+1;
l=r;
}
printf("%d %d\n",ans,(int)way.size());
for(auto t : way) printf("%d %d\n",t.first,t.second);
return 0;
}