CodeTON Round 1 (Div. 1 + Div. 2, Rated, Prizes!) 「A - F」

这场打的有点下饭 D竟然也是简单的分类讨论一下 只能说是不够冷静 没有仔细分析了

这几天没什么动力 也不想写什么东西 但这场比赛会补好 写完 因为白天闲时的一个念头

A. Good Pairs

找到最大的数和最小的数的位置 注意顺序 输出

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<bits/stdc++.h>
using namespace std;
int main() {
int tt;cin>>tt;
while(tt--){
int n;cin>>n;
int Max=-1,Min=INT_MAX,p1,p2;
for(int i=1;i<=n;i++) {
int tem;cin>>tem;
if(tem>Max) Max=tem,p1=i;
if(tem<Min) Min=tem,p2=i;
}
if(p1>p2) p1^=p2^=p1^=p2;
cout<<p1<<' '<<p2<<'\n';
}
return 0;
}

B. Subtract Operation

经过 n-1 次操作最后的答案值是最后两个减数的差 set维护一下就好

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<bits/stdc++.h>
using namespace std;
int main(){
int tt;cin>>tt;
while(tt--){
int n,k;cin>>n>>k;
vector a(n,0);
for(int i=0;i<n;i++) cin>>a[i];
set<int>s{a.begin(),a.end()};
bool flag=false;
for(auto it:s) if(s.find(it+k)!=s.end()) {flag=true; break;}
if(flag) puts("YES");
else puts("NO");
}
return 0;
}

C. Make Equal With Mod

考虑构造的方式: 找到最大的数k %k得到0 %k-1得到1

可以知道同时含有0 1的情况是处理不了的

那么如果1有出现过 就尝试把别的数变到1 就不能有连续的数字k和k+1

如果没有1 那么全部变到0 从大到小依次%当前的Max就可以做到

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<bits/stdc++.h>
using namespace std;
int main(){
int tt;cin>>tt;
while(tt--){
int n;cin>>n; vector<int>a(n);
bool f1=false;
for(int i=0;i<n;i++) {cin>>a[i]; if(a[i]==1) f1=true;}
multiset<int>s{a.begin(),a.end()};
bool F1=false;
if(f1) for(auto it:s) if(s.count(it+1)>=1) {F1=true; break;}
if(F1) {puts("NO"); continue;}
puts("YES");
}
return 0;
}

D. K-good

给定一个数$n$ 判断是不是$k$个数之和 并且这$k$个数$\mod k$构成$k$的完全剩余系

如果是 输出k 不是输出-1

事实上还是分类讨论 是我想歪了.. 首先很容易得到一个东西

  1. $n=[2g+(k-1)]*\frac{k}{2}\ \ if(k\mod 2=0)$

  2. $n=[g+\frac{k-1}{2}]*k\ \ if(k\mod 2=1)$

$g=\sum_{i=0}^{n-1}{a_i}$ 假设$b_i=a_i*k+i$且$n=\sum_{i=0}^{n-1}b_i$

额 然后我这里十分sb的去分解因数找k去了 结果不是边界搞错就是T.. 还在想$10^{18}$估计是要Pollard Rho..

事实上注意到肯定有一个因数是奇数 那么如果是2的次幂 就可以输出-1了

但是有了答案也不能草率的输出奇数或者对应的除数作为k 不然可能会有g等于0的情况

题设要求正整数 因此不能有0的出现 因此我们要求1 2式里乘号右边那东西尽量小

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int main(){
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int tt;cin>>tt;
while(tt--){
ll n;cin>>n; ll k=1;
while(n%2==0) n/=2,k*=2;
if(n==1) cout<<"-1\n";
else cout<<min(n,2*k)<<'\n';
}
}

E. Equal Tree Sums

给定n个点的树的连接情况 要求给每个点赋值$a_i$ 使得删除任意一个点之后 剩余的联通块权值和相等

要求$1\leq a_i\leq 10^5$

看了知乎几个回答 除了小岛的有分析之外别的就直接上染色+度数的方法了 然后再balabala事后分析一波 感觉不太好

这种事后分析感觉没什么帮助

首先随便画个链 可知需要设置负权 进一步能想到设置负权的原因是

假设都是正权 如果删除一个点之后 假设还剩两个联通块 如果权值能平衡 那么不删除这个这点 转而删除旁边的点

权值就势必不会平衡 进一步可知点权的贡献应该都视作-1或1

后面随便搞个Huffman树 完全二叉树 权值与度数关联这个比较好想 就是二分图染色可能是比较常用的套路 就是令一条边的影响为|1|

用度数和其相反数设置过去 一条边给两个点的贡献是-1+1=0 这样整张图的权值为0

在删除一个节点之后 设权值为k 会产生$|k|$个联通块 那么每个联通块在失去了当前节点之后 就会产生一个$-\frac{|k|}{k}$的权值

剩余的联通块于是就平衡了

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<bits/stdc++.h>
using namespace std;
int main() {
int tt;cin>>tt;
while(tt--){
int n;cin>>n;
vector e(n+1,vector<int>{});
vector ans(n+1,0);
for(int i=1;i<n;i++){ int u,v;cin>>u>>v; e[u].push_back(v),e[v].push_back(u); }
auto dfs = [&](auto self,int u,int f,int k)->void{
ans[u]=k*e[u].size();
for(auto it:e[u]){
if(it==f) continue;
self(self,it,u,-k);
}
};
dfs(dfs,1,0,1);
for(int i=1;i<=n;i++) cout<<ans[i]<<' ';
cout<<endl;
}
return 0;
}

F. Parametric MST

给定a_i表示点权 设图G(t)由n个点和边(i,j) 边权为$a_i*a_j+t(a_i+a_j)$组成

f(t)是G(t)的最小生成树的花费 要求找出f(t)的上界 或者是INF t在实数范围内

简单分析一下 边权只考虑$t*(a_i+a_j)$ 即$t \to +\infty | t\to -\infty$

如果所有的形态都是花费>0 或者<0 那么配上$t\to\infty$就会趋向INF 不然就确保有解

之后的就不会了 有二分的感觉 但是好像不行.. 后面的内容就属于是搬运题解了..

考虑固定t之后快速求出MST.. 边权变换为$(a_i+t)*(a_j+t)-t^2$ 删掉常数$t^2$ 答案就是新图的MST-$(n-1)t^2$

这个时候连边的方式就是$a_1$和$a_n$相连 然后$>0$的连向$a_1$否则$a_n$ 这个过程满足prime的要求 顺序无所谓

这个形态发生改变的过程在t取到$-a_i$的时候 然后更新上界就好了 整个过程的瓶颈在于排序

code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>=a;i--)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define il inline
#define rg register
#define SZ(x) ((int)(x).size())
#define all(x) x.begin(),x.end()
using namespace std;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef long long ll;
typedef double db;
const int INF=0x7fffffff;
const int inf=0x3f3f3f3f;
const int MOD=998244353;
const int mod=1e9+7;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; }
// head
template<typename T>
inline void scan(T &x) {
x=0; int f=1; char ch=getchar();
while(ch<'0'||ch>'9')
f=(ch=='-')?-1:1,ch=getchar();
while (ch>='0'&&ch<='9')
x=(x<<3)+(x<<1)+ch-48,ch=getchar();
x*=f;
}
void solve(){
int n;scan(n);
vector a(n+1,0LL),sum(n+1,0LL);
rep(i,1,n+1) scan(a[i]);
sort(a.begin()+1,a.begin()+1+n);
rep(i,1,n+1) sum[i]=sum[i-1]+a[i];
if(a[1]*(n-2)+sum[n]>0||a[n]*(n-2)+sum[n]<0){
puts("INF");
return;
}
ll ans=-2e18;
auto calc=[&](int pos,ll val)->ll{
return -(n-1)*val*val+(a[n]+val)*(pos*val+sum[pos])+(a[1]+val)*(sum[n-1]-sum[pos]+(n-1-pos)*val);
};
rep(i,1,n) ans=max(ans,calc(i,-a[i]));
cout<<ans<<endl;
}
int main() {
int tt;scan(tt);
while(tt--){
solve();
}
return 0;
}

之前看小岛的回答 说F的确有二分的做法 我没啥思路 网上找了一圈也没见着 小岛还删回答了 哎..


这感觉已经是Div. 1C的难度了 后面的题暂时不开了 补别的场次了..