CF1684
Codeforces Round 792 (Div. 1 + Div. 2)
CF1684A
CF1684A题意
有一个用十进制表示的没有前导零的正整数 n n n 。Alice 和 Bob 正在用这个数玩一个游戏。Alice 先手,他们轮流进行游戏。
在她的这一轮中,Alice 应该交换这个数中的任何不同位置的两位。轮到 Bob 时,他每次都会删除这个数的末一位。当这个数只剩一位时,游戏结束。
你需要找出 Alice 用最佳方法在最后找出的最小数。
CF1684A题解
如果只有两位数就没辙了,只能是最后一位。
否则就是最小的那一位。
CF1684A代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
int n;
char ch[100];
signed main(){
int T = read();
while(T--){
scanf("%s",ch + 1);
n = strlen(ch + 1);
if(n == 2){putchar(ch[2]);}
else{
int x = 10;
for(int i = 1;i <= n;i++)x = min(x,ch[i] - '0');
putchar(x + '0');
}
puts("");
}
return 0;
}
CF1684B
CF1684B题意
给你三个数字
a
,
b
,
c
(
a
<
b
<
c
)
a,b,c(a<b<c)
a,b,c(a<b<c),让你构造三个数字
x
,
y
,
z
x,y,z
x,y,z,满足
x
≡
a
(
m
o
d
y
)
x\equiv a\pmod y
x≡a(mody)
y
≡
b
(
m
o
d
z
)
y\equiv b\pmod z
y≡b(modz)
z
≡
c
(
m
o
d
x
)
z\equiv c\pmod x
z≡c(modx)
CF1684B题解
不难发现当 x = a + b + c , y = b + c , z = c x=a+b+c,y=b+c,z=c x=a+b+c,y=b+c,z=c 时满足条件,证明略。
CF1684B代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
signed main(){
int T = read();
while(T--){
int a = read(), b = read(), c = read();
printf("%lld %lld %lld\n",a + b + c,b + c,c);
}
return 0;
}
CF1684C
link
当前用时:33:34.25
CF1684C题意
如果一个表格每行都单调不降,称它为好的。
给你
t
t
t 个
n
i
n_i
ni 行
m
i
m_i
mi 列的表格,对于每个表格,询问是否能通过调换某两列 (不一定不同) 使得这个表格是好的(这样的操作需要且仅能执行一次)。如果可以,输出两列的编号;不可以,输出
−
1
-1
−1。
CF1684C题解
先排序,然后看哪里不同就交换哪里,然后只能操作一次,交换完了在看下是不是正确的即可。
CF1684C代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 2e5 + 10;
vector<int> vec[maxn], srt[maxn];
int n, m;
void solve(){
n = read(); m = read();
for(int i = 1;i <= n;i++){
vec[i].clear();vec[i].push_back(-114514);
for(int j = 1;j <= m;j++)vec[i].push_back(read());
srt[i] = vec[i];
sort(srt[i].begin(),srt[i].end());
}
int pos0 = -1, pos1 = -1;
for(int i = 1;i <= n;i++){
for(int j = 1;j <= m;j++){
if(srt[i][j] != vec[i][j]){
if(pos0 == -1){pos0 = j;continue;}
if(pos1 == -1){pos1 = j;break;}
}
}
if(pos0 != -1 && pos1 != -1)break;
}
for(int i = 1;i <= n;i++){
swap(vec[i][pos0],vec[i][pos1]);
for(int j = 1;j <= m;j++)
if(vec[i][j] != srt[i][j]){puts("-1");return;}
}
if(pos0 == -1 && pos1 == -1)pos0 = pos1 = 1;
printf("%d %d\n",pos0, pos1);
}
signed main(){
int T = read();
while(T--)solve();
return 0;
}
CF1684D
link
当前用时:47:52.82
Remote Judge 又双叒叕炸了,好似
CF1684D题意
这里有 n n n 个陷阱,你需要按照给出的顺序穿过这些陷阱,每个陷阱将会对你造成 a i a_i ai 的伤害
你有至多 k k k 次机会跳过这些陷阱,可以避免你所跳过的陷阱给你造成的伤害,不过接下来的所有陷阱都会给你多造成 1 1 1 点伤害
跳过陷阱所造成的额外伤害会叠加,如果你当前已经跳过了 3 3 3 个陷阱,接下来的陷阱给你造成的伤害将会是 a i + 3 a_i +3 ai+3
现在需要你求出受到的最小伤害
CF1684D题解
首先,如果只考虑跳一次陷阱,这道题就很简单了。
答案是
∑
i
=
1
n
a
i
\sum_{i=1}^na_i
∑i=1nai
−
max
i
=
1
n
(
a
i
−
n
+
i
)
-\max_{i=1}^n(a_i-n+i)
−maxi=1n(ai−n+i) 对吧。
那如果可以跳很多次呢?
答案是前
k
k
k 大的
a
i
−
n
+
i
a_i-n+i
ai−n+i 吗?
好像不对吧,举个栗子,你在
7
,
2
7,2
7,2 两个位置分别跳了一次,那么在
2
2
2 位置计算的答案就应该是
a
2
−
n
+
2
+
1
a_2-n+2+1
a2−n+2+1,因为在
7
7
7 位置是不受伤的。
将结论扩展到
k
k
k 个数,如果先不考虑后来躲开的伤害对前面的影响,那么答案就是
∑
i
=
1
n
a
i
\sum_{i=1}^na_i
∑i=1nai 减去前
k
k
k 大的
(
a
i
−
n
+
i
)
(a_i-n+i)
(ai−n+i)。
如果考虑后来躲开的伤害呢?
从排序第一个躲闪开始看起,它后面的
k
−
1
k-1
k−1 个躲闪对它的贡献是
1
−
k
1-k
1−k,第二个同理。
然后最终答案就是
∑
i
=
1
n
a
i
\sum_{i=1}^na_i
∑i=1nai 减去前
k
k
k 大的
(
a
i
−
n
+
i
)
(a_i-n+i)
(ai−n+i) 再减去
k
×
(
k
−
1
)
2
\frac{k\times (k-1)}{2}
2k×(k−1)
CF1684D代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 2e5 + 10;
int n, k;int a[maxn];
priority_queue<int,vector<int>,less<int> > que;
signed main(){
int T = read();
while(T--){
n = read(); k = read();
while(!que.empty())que.pop();
int ans = 0;
for(int i = 1;i <= n;i++){
ans += (a[i] = read());
que.push(a[i] - (n - i));
}
for(int i = 1;i <= k;i++){ans -= que.top();que.pop();}
printf("%lld\n",ans - k * (k - 1) / 2);
}
return 0;
}
CF1684E
link
当前用时:2:27:18.34
另:有的时候停下来想想会有意想不到的收获。
CF1684E题意
给你一个大小为n的数组a,保证数组内元素非负,你可以执行以下操作k次:
在一次操作中将数组内任意一个数字改为任何一个非负整数。
现在定义这个数组的成本为
D
I
F
F
(
a
)
−
M
E
X
(
a
)
DIFF(a)−MEX(a)
DIFF(a)−MEX(a),其中
D
I
F
F
(
a
)
DIFF(a)
DIFF(a) 为
a
a
a 数组内元素去重后的数量,
M
E
X
(
a
)
MEX(a)
MEX(a) 为数组中未出现的元素中最小的元素。
举个例子,
M
E
X
(
1
,
2
,
3
)
=
0
MEX( { 1 , 2 , 3 } )=0
MEX(1,2,3)=0,
M
E
X
(
0
,
1
,
2
,
4
,
5
)
=
3
MEX( { 0 , 1 , 2 , 4 , 5 } ) = 3
MEX(0,1,2,4,5)=3。
现在给你数组 a a a,求能实现的最小成本。
CF1684E题解
这很好WA,爱来自对拍
首先想一个问题,对于一个确定的数组,答案是什么?
不难发现,如果将数组排好序之后,答案就是
D
i
f
[
M
e
x
(
a
)
,
I
N
F
]
Dif[Mex(a),INF]
Dif[Mex(a),INF]。
其中
D
i
f
[
l
,
r
]
Dif[l,r]
Dif[l,r] 表示将数组中值域为
[
l
,
r
]
[l,r]
[l,r] 之间的数字取出得到新的数组
b
b
b,
D
I
F
F
(
b
)
DIFF(b)
DIFF(b) 的值。
如果以值域为
x
x
x 轴,以出现次数为
y
y
y 轴,那么对于每个数组中出现的数字
x
x
x,作矩形
(
x
,
0
)
→
(
x
,
c
n
t
x
)
(x,0)\to(x,cnt_x)
(x,0)→(x,cntx),然后就会发现,这玩应好像堆叠积木块。
如果称一个从
(
x
,
0
)
→
(
x
,
k
)
(x,0)\to(x,k)
(x,0)→(x,k) 的矩形叫做一个塔,答案就变成了
[
M
e
x
(
a
)
,
I
N
F
]
[Mex(a),INF]
[Mex(a),INF] 的塔的数量。
如果设当前数组的
M
e
x
(
a
)
=
t
Mex(a)=t
Mex(a)=t。
不难想到,每次操作有两种选择:
- 将 [ t , I N F ] [t,INF] [t,INF] 值域范围内的某个数字变成 t t t,对应在平面中就是取出在 [ t , I N F ] [t,INF] [t,INF] 中的一个积木块,放到 t t t 位置,称之为铺路。此时 t ← t + 1 t\gets t+1 t←t+1。
- 将任意一个数字变成已经出现的另一个数字,对应在平面上就是将某一个积木块放在其他的积木块之上,称之为堆叠
想想这两个操作的贡献:
- 铺路:如果拿出积木块后塔少了一个(就是这个积木块自己组成了一个塔),那么造成 − 1 -1 −1 贡献,否则贡献是 0 0 0。
- 堆叠:同铺路。
这时不难发现,与其进行堆叠,不如直接铺路,因为铺路可能会扩展更多的塔联通,而堆叠没有。
这思路不就出来了,先排序,然后先
f
o
r
for
for 一遍看看能铺到哪里(就是
M
e
x
(
a
)
Mex(a)
Mex(a) 最大是多少),然后每次从塔最矮的开始铺路。
然后就完了~~~~吗?
亿堆细节慢慢调吧。
CF1684E代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 1e5 + 10;
int n, k;
int a[maxn];
int buc[maxn];
signed main(){
int T = read();
while(T--){
n = read();int t = k = read();
for(int i = 1;i <= n;i++)a[i] = read();
sort(a + 1,a + 1 + n);
int rp = n, i = 1;
a[n + 1] = 0x3f3f3f3f;a[0] = -1;
for(i = 0;i <= rp && k > 0;i++){
if(a[i + 1] - a[i] <= 1)continue;
if(a[i + 1] - a[i] - 1 > k)break;
while(a[i + 1] - a[i] > 1 && rp > i && k > 0){rp--;k--;a[i]++;}
// if(a[i + 1] - a[i] > 1)break;
}
while(a[i + 1] - a[i] <= 1 && i <= n)i++;i++;
// for(int i = 1;i <= n;i++){printf("a[%d]=%d ",i,a[i]);}
// printf("\nrp = %d,i = %d\n",rp,i);
int cnt = 1, l = i, mx = 0;
if(i > rp){puts("0");continue;}
memset(buc,0,sizeof(buc));
for(int i = l;i <= n;i++){
if(a[i] != a[i + 1]){buc[cnt]++;mx = max(mx,cnt);cnt = 1;}
else{cnt++;}
}
i = 1;
while(t && i <= mx){
if(buc[i] <= t / i){t -= buc[i] * i;buc[i] = 0;}
else{buc[i] -= t / i;t = 0;break;}
i++;
}
cnt = 0;
for(int i = 1;i <= mx;i++){cnt += buc[i];}
printf("%d\n",cnt);
}
return 0;
}
CF1684F
link
当前用时:已超时。
CF1684F题意
给定长度为
n
n
n 的序列
a
a
a,以及
m
m
m 个数对
(
l
i
,
r
i
)
(l_i,r_i)
(li,ri)。
你可以进行下列操作至多一次:
- 选择序列 a a a 的一个子段,并将其中的每个元素的值都改成任意整数。
你需要保证执行完操作之后,对于每个整数
i
(
1
≤
i
≤
m
)
i(1\leq i\leq m)
i(1≤i≤m),都有
a
[
l
i
,
r
i
]
a[l_i,r_i]
a[li,ri] 中所有元素互不相同。
你需要最小化操作时选择的子段的长度,并求出这个长度的最小值。
特别的如果没有必要进行操作,答案为
0
0
0。
CF1684F题解
破防,BYD
调了
3
h
3h
3h 才调出来。
菜就多练练。
不难想到,首先将
m
m
m 个数对转化成对每个点
i
i
i 的限制。
如果设
l
i
m
i
lim_i
limi 表示
[
l
i
m
i
,
i
]
[lim_i,i]
[limi,i] 之间不得有重复数字,那么
l
i
m
i
=
min
1
≤
j
≤
m
i
∈
[
l
j
,
r
j
]
l
j
lim_i=\min_{1\le j\le m}^{i\in[l_j,r_j]}l_j
limi=min1≤j≤mi∈[lj,rj]lj。
不难发现,这个
l
i
m
i
lim_i
limi 满足单调不减(后面要考)。
然后分开不同的数字,将同一个数字的下标存在一起(设存在
v
e
c
a
i
vec_{a_i}
vecai数组中)。
然后设
[
L
i
,
R
i
]
[L_i,R_i]
[Li,Ri] 表示如果不选择
i
i
i 在所求区间中时,
[
L
i
,
R
i
]
[L_i,R_i]
[Li,Ri] 中的每一个数字都要选择。
显然
L[i] = vec[a[i]][lower_bound(vec[a[i]].begin(),vec[a[i]].end(),lim[i]) - vec[a[i]].begin()];
R[i] = vec[a[i]][upper_bound(vec[a[i]].begin(),vec[a[i]].end(),i - 1) - vec[a[i]].begin() - 1];
然后坑爹的来了(也是我调了三个小时的原因)
对于以上这两样代码算出的
L
i
,
R
i
L_i,R_i
Li,Ri,需要进行边界判定:如果
l
i
m
i
=
n
+
1
lim_i=n+1
limi=n+1 或者
L
i
>
R
i
L_i>R_i
Li>Ri(尤其是这个),则
L
i
=
n
+
1
,
R
i
=
0
L_i=n+1,R_i=0
Li=n+1,Ri=0,即一定成立。
然后问题就变成了给你
n
n
n 个点,可以选择一个区间,这个区间中的所有点都会被选中,并且每个点要不然选择自己,要不然选择所有
[
L
i
,
R
i
]
[L_i,R_i]
[Li,Ri] 中的点,问这个区间最短是多少?
先说一个问题,就是是否存在一种情况,在其他条件满足的情况下,你选择了
i
i
i 但没选择
j
(
i
<
j
)
j(i<j)
j(i<j),这时
i
i
i 的条件满足了,但是
j
j
j 的没满足?
显然不存在,因为前文说到
l
i
m
i
lim_i
limi 单调不减,也就是
j
j
j 对
i
i
i 的限制不强于
i
i
i 自己的限制,故一定满足。
并且由于
l
i
m
i
lim_i
limi 单调不减,故
L
i
L_i
Li 也单调不减,也就是说,如果使用双指针的话,左端点右移并不会导致右端点左移也能够成立。
所以线段树维护没有选择的所有点的
L
L
L 最小值和
R
R
R 最大值+双指针即可。
当然,如果你愿意,也可以预处理
[
1
,
i
]
[1,i]
[1,i] 的最小最大值和
[
i
,
n
]
[i,n]
[i,n] 的最小最大值,达到
O
(
n
)
O(n)
O(n) 的时间复杂度。
CF1684F代码
// LUOGU_RID: 137133385
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 3e5 + 10;
int n, a[maxn], m;
int L[maxn], R[maxn];
int lim[maxn];
vector<int> vec[maxn];
struct Segment_Tree{
struct node{
int maxx, minn;
node(int maxx = 0,int minn = 0):maxx(maxx),minn(minn){}
}d[maxn << 2];
node mergenode(node l,node r){return node(max(l.maxx,r.maxx),min(l.minn,r.minn));}
void build(int l,int r,int p){
if(l == r){d[p] = node(R[l],L[l]);return;}
int mid = l + r >> 1;
build(l,mid,p << 1);build(mid + 1,r,p << 1 | 1);
d[p] = mergenode(d[p << 1],d[p << 1 | 1]);
}
void update(int l,int r,int pos,int p,int x,int y){
if(l == r && l == pos){d[p] = node(y,x);return;}
int mid = l + r >> 1;
if(pos <= mid)update(l,mid,pos,p << 1,x,y);
else update(mid + 1,r,pos,p << 1 | 1,x,y);
d[p] = mergenode(d[p << 1],d[p << 1 | 1]);
}
node query(int l,int r,int s,int t,int p){
if(s <= l && r <= t)return d[p];
int mid = l + r >> 1;
if(t <= mid)return query(l,mid,s,t,p << 1);
if(mid < s)return query(mid + 1,r,s,t,p << 1 | 1);
return mergenode(query(l,mid,s,t,p << 1),query(mid + 1,r,s,t,p << 1 | 1));
}
void update(int pos,int x,int y){update(1,n,pos,1,x,y);}
}tree;
inline bool check(int l,int r){
Segment_Tree::node tmp = tree.query(1,n,1,n,1);
return l <= tmp.minn && tmp.maxx <= r;
}
map<int,int> mp;int tot;
signed main(){
int T = read();
while(T--){
n = read(); m = read();int u, v;mp.clear();tot = 0;
for(int i = 1;i <= n;i++){
a[i] = read(); if(!mp[a[i]])mp[a[i]] = ++tot;
a[i] = mp[a[i]]; vec[i].clear();
lim[i] = L[i] = R[i] = 0;
}
for(int i = 1;i <= m;i++){
u = read(); v = read();
vec[v].push_back(u);
}
u = n + 1;
for(int i = n;i;i--){
for(int j : vec[i]){u = min(u,j);}
lim[i] = u;vec[i].clear();
}
for(int i = 1;i <= n;i++)vec[a[i]].push_back(i);
for(int i = 1;i <= tot + 1;i++)vec[i].push_back(n + 1);
for(int i = 1;i <= n;i++){
if(lim[i] < i && i != vec[a[i]][0]){
L[i] = vec[a[i]][lower_bound(vec[a[i]].begin(),vec[a[i]].end(),lim[i]) - vec[a[i]].begin()];
R[i] = vec[a[i]][upper_bound(vec[a[i]].begin(),vec[a[i]].end(),i - 1) - vec[a[i]].begin() - 1];
}
else L[i] = n + 1;
if(L[i] == n + 1 || R[i] < L[i])R[i] = 0, L[i] = n + 1;
// printf("%d %d %d\n",i,L[i],R[i]);
}
u = 1; v = 0;tree.build(1,n,1);
int ans = n, ll = 0, rr = 0;
for(;u <= n;u++){
while(v < n && !check(u, v)){v++;tree.update(v,n + 1,0);}
if(check(u, v)){
if(ans >= max(0,v - u + 1)){
ans = max(0,v - u + 1);
ll = u; rr = v;
}
}
// if(a[1] == 827218149){
// if(u == 83119)printf("u = %d,v = %d ans = %d\n",u,v,ans);
// if(u == 53887)printf("u = %d,v = %d ans = %d\n",u,v,ans);
// }
tree.update(u,L[u],R[u]);
}
// if(ans == 62326)printf("l = %d, r = %d\n",ll,rr);
printf("%d\n",ans);
// for(int i = 3133;i <= 3140;i++)printf("a[%d]=%d\n",i,a[i]);
}
return 0;
}
CF1684G
CF1684G题意
下面是一个经过部分改动的求 gcd \gcd gcd 的伪代码(其中 t t t 是一个初始为空的序列):
function Euclid(a, b):
if a < b:
swap(a, b)
if b == 0:
return a
r = reminder from dividing a by b (即设 r 为 a mod b)
if r > 0:
append r to the back of t (即将 r 插入到 t 的尾部)
return Euclid(b, r)
有一个由数对构成的序列
p
p
p,接下来我们对
p
p
p 中每个数对都执行一次上述函数,然后把
t
t
t 重新排列并给定到输入中。
给定
n
,
m
n,m
n,m 和长度为
n
n
n 的序列
t
t
t。
你需要构造一个长度不超过
2
×
1
0
4
2\times10^4
2×104 的数对序列
p
p
p,满足:
- 每个数对中的元素都是不超过 m m m 的正整数。
- 根据序列 p p p 可以经过上述操作得到输入中给定的 t t t。
有解输出任意一组解,无解输出 -1
。
CF1684G题解
身为网络流睪手的我当然是被秒啦(bushi)
不难想到,如果用
(
3
t
,
2
t
)
(3t,2t)
(3t,2t) 这样的数对,就能构造出
t
t
t 这个数字,且不会附带出其他的数字。
所以对于
t
i
≤
m
3
t_i\le \frac{m}{3}
ti≤3m 的数字显然可以直接构造。
称
a
≤
m
3
a\le\frac{m}{3}
a≤3m 的数
a
a
a 为小数,否则为大数
那剩下的
(
m
3
,
m
]
(\frac{m}{3},m]
(3m,m] 怎么办呢?
不难发现,如果用诸如
(
2
t
+
a
,
t
+
a
)
(2t+a,t+a)
(2t+a,t+a) 这样的数对,就能同时构造
t
,
a
t,a
t,a 这两个数字,如果还能保证
a
∣
t
a|t
a∣t,那就只会构造出这两个数字。
但是同样发现,如果有一个数
t
i
>
m
2
t_i>\frac{m}{2}
ti>2m,那一定无解,因为在这个代码中,一定是大数模小数,如果想构造出
t
i
>
m
2
t_i>\frac{m}{2}
ti>2m,那就一定有
x
=
y
+
k
t
i
x=y+kt_i
x=y+kti,其中
k
∈
Z
且
k
≥
1
k\in \mathbb{Z}且k\ge1
k∈Z且k≥1,
y
>
k
i
y>k_i
y>ki,故
min
x
=
y
+
t
i
>
m
\min x=y+t_i>m
minx=y+ti>m 无解。
然后不难发现,如果用
(
2
t
+
a
,
t
+
a
)
(2t+a,t+a)
(2t+a,t+a) 这样的数对,来构造
t
,
a
t,a
t,a 这两个数字,那一定是
t
>
m
3
t>\frac{m}{3}
t>3m,不然自己构造自己的就好了。
因为
t
>
m
3
t>\frac{m}{3}
t>3m,由
2
t
+
a
≤
m
2t+a\le m
2t+a≤m 得
a
≤
m
3
a\le\frac{m}{3}
a≤3m。
一个大数匹配一个小数,二分图匹配!
于是每个大数
t
i
t_i
ti 尝试匹配一个小数
t
j
t_j
tj,满足
t
j
∣
t
i
t_j|t_i
tj∣ti 且
2
t
i
+
t
j
≤
m
2t_i+t_j\le m
2ti+tj≤m。
跑二分图匹配即可。
CF1684G代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x = 0, f = 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 1e3 + 10;
int n, m;
int a[maxn];
int mn[maxn], mx[maxn], tot1, tot2;
int match[maxn];
bool book[maxn];
bool dfs(int u){
for(int v = 1;v <= tot1;v++){
if(mx[u] % mn[v] == 0 && !book[v] && mx[u] * 2 + mn[v] <= m){
book[v] = 1;
if(!match[v] || dfs(match[v])){
match[v] = u;
return true;
}
}
}
return false;
}
vector<pair<int,int> > vec;
signed main(){
n = read(); m = read();
for(int i = 1;i <= n;i++)a[i] = read();
sort(a + 1,a + 1 + n);
if(a[n] > m / 2){puts("-1");return 0;}
for(int i = 1;i <= n;i++){
if(a[i] <= m / 3)mn[++tot1] = a[i];
else mx[++tot2] = a[i];
}
if(tot1 < tot2){puts("-1");return 0;}
for(int i = 1;i <= tot2;i++){
for(int j = 1;j <= tot1;j++)book[j] = 0;
if(!dfs(i)){puts("-1");return 0;}
}
for(int i = 1;i <= tot1;i++){
if(!match[i])vec.push_back(make_pair(mn[i] * 3,mn[i] * 2));
else vec.push_back(make_pair(mx[match[i]] + mx[match[i]] + mn[i],mx[match[i]] + mn[i]));
}
printf("%d\n",vec.size());
for(auto i : vec)printf("%d %d\n",i.first,i.second);
return 0;
}
CF1684H
*3400不会QWQ
菜就多练练pwp