目录
- 前言
- 题解部分
- B Ichihime and Triangle(800)
- 题目大意
- 题解
- 代码实现
- C Kana and Dragon Quest game(900)
- 题目大意
- 题解
- 代码实现
- J Squares and Cubes(800)
- 题目大意
- 题解
- 代码实现
- F Double Sort(1200)
- 题目大意
- 题解
- 代码实现
- I Minimize the Thickness(1100)
- 题目大意
- 题解
- 代码实现
- K Find the Spruce(1400)
- 题目大意
- 题解
- 代码实现
- E Xenia and Colorful Gems(1700)
- 题目大意
- 题解
- 代码实现
- L Floor and Mod(1700)
- 题目大意
- 题解
- 代码实现
- M Divide and Summarize(1600)
- 题目大意
- 题解
- 代码实现
- D Linova and Kingdom(1600)
- 题目大意
- 题解
- 代码实现
- G Permutation Restoration(1900)
- 题目大意
- 题解
- 代码实现
- A Card Game(1500)
- 题目大意
- 题解
- 代码实现
- H Maximum AND(1800)
- 题目大意
- 题解
- 代码实现
前言
感谢 shstyle 为本场训练赛挑选题目
以笔者作为参与者的个人视角,本场题目难度如下(题目颜色采用luogu分级,数字为cf题目评分)
Easy:BCJ
Mid:FIK
Mid hard:ELMD
Hard:GAH
题解部分
B Ichihime and Triangle(800)
原题链接:https://codeforces.com/problemset/problem/1337/A
题目大意
给定四个数
a
a
a,
b
b
b,
c
c
c,
d
d
d,输出三个数
x
x
x,
y
y
y,
z
z
z使
a
≤
x
≤
b
,
b
≤
y
≤
c
,
c
≤
z
≤
d
a \leq x \leq b,b \leq y \leq c,c \leq z \leq d
a≤x≤b,b≤y≤c,c≤z≤d且能组成以
x
x
x,
y
y
y,
z
z
z为边的三角形,保证一定有解
题解
#include<bits/stdc++.h>
#define INF 2147483647LL
#define int long long
#define MAXN 300005
#define mod 1000000007
#define PI 3.14
#define eps 1e-10
#define pa pair<int,int>
#define ms(a,x) memset(a,x,sizeof(a))
#define mc(ar1,ar2) memcpy(ar1,ar2,sizeof(ar2))
#define mkp(a,b) make_pair(a,b)
#define ls (p<<1)
#define rs (p<<1|1)
#define fn(i,st,ed) for(int i=st;i<=ed;++i)
#define fd(i,st,ed) for(int i=st;i>=ed;--i)
#define fg(i,x,head,e) for(int i=head[x];~i;i=e[i].nxt)
using namespace std;
inline int read(){int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;}
void solve(){
int a,b,c,d;cin>>a>>b>>c>>d;
cout<<b<<' '<<c<<' '<<c<<endl;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--){solve();}
return 0;
}
代码实现
关键点在于保证一定有解,又因为三角形两边之和大于第三边,两边之差小于第三边,所以较小的
x
x
x和
y
y
y取上边界,较大的
z
z
z取下边界即是正解
时间复杂度
O
(
1
)
O(1)
O(1)
C Kana and Dragon Quest game(900)
原题链接:https://codeforces.com/problemset/problem/1337/B
题目大意
给定数字 x x x, n n n, m m m,现在有两种操作,一种是使 x = ⌊ x 2 ⌋ + 10 x= \left\lfloor\dfrac{x}{2}\right\rfloor+10 x=⌊2x⌋+10,最多能进行 n n n次该操作,一种是使 x = x − 10 x=x-10 x=x−10,最多能进行 m m m次该操作,问是否能通过上述操作使得 x ≤ 0 x\leq0 x≤0
题解
显然对于操作
1
1
1,
x
x
x先减后增,不难求出阈值点是
x
=
20
x=20
x=20,而操作
2
2
2是单调减的,所以可以先将操作
1
1
1尽量做,达到阈值之后进行操作
2
2
2,模拟上述过程,最后判断即可
时间复杂度
O
(
n
+
m
)
O(n+m)
O(n+m)
代码实现
#include<bits/stdc++.h>
#define INF 2147483647LL
#define int long long
#define MAXN 300005
#define mod 1000000007
#define PI 3.14
#define eps 1e-10
#define pa pair<int,int>
#define ms(a,x) memset(a,x,sizeof(a))
#define mc(ar1,ar2) memcpy(ar1,ar2,sizeof(ar2))
#define mkp(a,b) make_pair(a,b)
#define ls (p<<1)
#define rs (p<<1|1)
#define fn(i,st,ed) for(int i=st;i<=ed;++i)
#define fd(i,st,ed) for(int i=st;i>=ed;--i)
#define fg(i,x,head,e) for(int i=head[x];~i;i=e[i].nxt)
using namespace std;
inline int read(){int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;}
void solve(){
int x,n,m;cin>>x>>n>>m;
while(x>=20&&n)x=(x>>1)+10,n--;
while(x>=0&&m)x-=10,m--;
if(x<=0)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--){solve();}
return 0;
}
J Squares and Cubes(800)
原题链接:https://codeforces.com/problemset/problem/1619/B
题目大意
输出小于等于 n n n的完全平方数和完全立方数的数量和
题解
容斥原理,答案输出
n
+
n
3
−
n
6
\sqrt{n}+\sqrt[3]{n}-\sqrt[6]{n}
n+3n−6n,立方根可以用
n
3
\sqrt[3]{n}
3n预处理,也可以使用pow函数,且pow函数更为简洁,但是会需要自行调整精度
时间复杂度
O
(
n
3
)
O(\sqrt[3]{n})
O(3n)或
O
(
1
)
O(1)
O(1)
代码实现
#include<bits/stdc++.h>
#define INF 2147483647LL
#define int long long
#define MAXN 300005
#define mod 1000000007
#define PI 3.14
#define eps 1e-10
#define pa pair<int,int>
#define ms(a,x) memset(a,x,sizeof(a))
#define mc(ar1,ar2) memcpy(ar1,ar2,sizeof(ar2))
#define mkp(a,b) make_pair(a,b)
#define ls (p<<1)
#define rs (p<<1|1)
#define fn(i,st,ed) for(int i=st;i<=ed;++i)
#define fd(i,st,ed) for(int i=st;i>=ed;--i)
#define fg(i,x,head,e) for(int i=head[x];~i;i=e[i].nxt)
using namespace std;
inline int read(){int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;}
void solve(){
double n;cin>>n;n+=0.00001;
cout<<(int)pow(n,1.0/2)+(int)pow(n,1.0/3)-(int)pow(n,1.0/6)<<endl;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--){solve();}
return 0;
}
F Double Sort(1200)
原题链接:https://codeforces.com/problemset/problem/1681/C
题目大意
给定两个长度均为 n n n的序列 a a a和 b b b,可以对任意 a i a_i ai, a j a_j aj ( i ≠ j ) (i\neq j) (i=j) 进行交换,同时交换 b i b_i bi, b j b_j bj,询问是否能通过不超过 1 0 4 10^4 104次交换使得 a a a和 b b b全部从小到大排列,输出需要进行的操作数量以及需要进行的操作,若有多组答案,输出任意一组,若无法达到,则输出 − 1 -1 −1
题解
先考虑若必定有答案时的答案方案,由
n
≤
100
n\leq100
n≤100可知
n
2
≤
1
0
4
n^2\leq10^4
n2≤104,而冒泡排序的操作次数最多为
n
(
n
−
1
)
2
\frac{n(n-1)}{2}
2n(n−1)次,所以只要输出冒泡排序的交换次数和双方即可
再考虑什么情况下无法达到,由冒泡排序过程可知,若任意一次交换中
a
a
a和
b
b
b对应数对大小关系不相同,则无法达到要求,输出
−
1
-1
−1
最终时间复杂度
O
(
n
2
)
O(n^2)
O(n2)
代码实现
#include<bits/stdc++.h>
#define INF 2147483647LL
#define int long long
#define MAXN 300005
#define mod 1000000007
#define PI 3.14
#define eps 1e-10
#define pa pair<int,int>
#define ms(a,x) memset(a,x,sizeof(a))
#define mc(ar1,ar2) memcpy(ar1,ar2,sizeof(ar2))
#define mkp(a,b) make_pair(a,b)
#define ls (p<<1)
#define rs (p<<1|1)
#define fn(i,st,ed) for(int i=st;i<=ed;++i)
#define fd(i,st,ed) for(int i=st;i>=ed;--i)
#define fg(i,x,head,e) for(int i=head[x];~i;i=e[i].nxt)
using namespace std;
inline int read(){int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;}
struct node{int a,b,id;}a[MAXN],stp[MAXN];
bool cmp(node x,node y){return x.a==y.a?x.b<y.b:x.a<y.a;}
void solve(){
int n,ans=0;cin>>n;
fn(i,1,n)cin>>a[i].a,a[i].id=i;
fn(i,1,n)cin>>a[i].b;
sort(a+1,a+1+n,cmp);
fn(i,1,n-1)if(a[i].a>a[i+1].a||a[i].b>a[i+1].b){cout<<-1<<endl;return;}
fn(i,1,n-1)fn(j,i+1,n){
if(a[i].id>a[j].id){
swap(a[i],a[j]);
stp[++ans]={i,j};
}
}
if(ans>10000){cout<<-1<<endl;return;}
cout<<ans<<endl;
fd(i,ans,1)cout<<stp[i].a<<" "<<stp[i].b<<endl;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--){solve();}
return 0;
}
I Minimize the Thickness(1100)
原题链接:https://codeforces.com/problemset/problem/1741/C
题目大意
给定长度为 n n n的序列 a a a,将 a a a分为若干个和相同的连续子序列,求最大子序列的最小值
题解
显然,对于任意分割状态,第一个连续子序列必定以
a
1
a_1
a1为第一个元素,可以枚举第一个子序列的长度
l
e
n
len
len,然后向后延伸,检查后面是否能恰好分割出若干个和为
s
u
m
[
l
e
n
]
sum[len]
sum[len]的子序列(
s
u
m
sum
sum为前缀和),若能则与答案取最小值
时间复杂度
O
(
n
)
O(n)
O(n)
代码实现
#include<bits/stdc++.h>
#define INF 2147483647LL
#define int long long
#define MAXN 300005
#define mod 1000000007
#define PI 3.14
#define eps 1e-10
#define pa pair<int,int>
#define ms(a,x) memset(a,x,sizeof(a))
#define mc(ar1,ar2) memcpy(ar1,ar2,sizeof(ar2))
#define mkp(a,b) make_pair(a,b)
#define ls (p<<1)
#define rs (p<<1|1)
#define fn(i,st,ed) for(int i=st;i<=ed;++i)
#define fd(i,st,ed) for(int i=st;i>=ed;--i)
#define fg(i,x,head,e) for(int i=head[x];~i;i=e[i].nxt)
using namespace std;
inline int read(){int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;}
int a[MAXN],sum[MAXN];
void solve(){
int n,now=0,ans;cin>>n;ans=n;
fn(i,1,n)cin>>a[i],sum[i]=sum[i-1]+a[i];
fn(i,1,n){
int tmp=i,nowsum=0,len=0;
fn(j,i+1,n){
nowsum+=a[j],len++;
if(nowsum==sum[i]){
//cerr<<now<<endl;
tmp=max(tmp,len),nowsum=0,len=0;
continue;
}
if(nowsum>sum[i]){nowsum=-1;break;}
}
if(nowsum<sum[i]&&nowsum!=0)continue;
if(nowsum!=-1)ans=min(ans,tmp);
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--){solve();}
return 0;
}
K Find the Spruce(1400)
原题链接:https://codeforces.com/problemset/problem/1461/B
题目大意
查询一张
n
×
m
n\times m
n×m大小的图上有多少由“ * ”组成的如下图的等腰三角形,可以重叠,“ * ”也是一个答案
题解
观察图像可知所有等腰三角形都是由一个“ * ”向下向外延伸,纵向向下一格,横向向两个方向各扩展一格,所以可以
n
m
nm
nm枚举每个点,对每个点进行延伸,每延伸一次答案就+1,对于横行是否合法可以维护前缀和来判断
时间复杂度
O
(
n
2
m
)
O(n^2m)
O(n2m)
代码实现
#include<bits/stdc++.h>
#define INF 2147483647LL
#define int long long
#define MAXN 300005
#define mod 1000000007
#define PI 3.14
#define eps 1e-10
#define pa pair<int,int>
#define ms(a,x) memset(a,x,sizeof(a))
#define mc(ar1,ar2) memcpy(ar1,ar2,sizeof(ar2))
#define mkp(a,b) make_pair(a,b)
#define ls (p<<1)
#define rs (p<<1|1)
#define fn(i,st,ed) for(int i=st;i<=ed;++i)
#define fd(i,st,ed) for(int i=st;i>=ed;--i)
#define fg(i,x,head,e) for(int i=head[x];~i;i=e[i].nxt)
using namespace std;
inline int read(){int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;}
string mp[505];
int sum[505][505];
bool check(int x,int l,int r){
if(sum[x][r]-sum[x][l-1]!=(r-l+1))return 0;
return 1;
}
void solve(){
int n,m;cin>>n>>m;
fn(i,1,n)cin>>mp[i];
fn(i,1,n)fn(j,0,m-1)sum[i][j]=sum[i][j-1]+(mp[i][j]=='*');
int ans=0;
fn(i,1,n)fn(j,0,m-1)if(mp[i][j]=='*'){
ans++;
int maxx=1,maxy=1;
while(i+maxx<=n&&j-maxy>=0&&j+maxy<m&&check(i+maxx,j-maxy,j+maxy))ans++,maxx++,maxy++;
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--){solve();}
return 0;
}
E Xenia and Colorful Gems(1700)
原题链接:https://codeforces.com/problemset/problem/1336/B
题目大意
现在有三堆石头,分别有 n r n_r nr, n g n_g ng, n b n_b nb个,每个石头有自己的价值,从每一堆中拿出一个石头,记价值分别为 x x x, y y y, z z z,求 ( x − y ) 2 + ( x − z ) 2 + ( y − z ) 2 (x-y)^2+(x-z)^2+(y-z)^2 (x−y)2+(x−z)2+(y−z)2的最小值
题解
不妨设
x
≤
y
≤
z
x\leq y\leq z
x≤y≤z,若已知中间值
y
y
y,则可以通过lowerbound轻松求出
x
x
x所在数组小于等于
y
y
y的最大值
x
x
x与
y
y
y所在数组大于等于
y
y
y的最小值
z
z
z,所以只需要确定数组顺序和中间值,这里可以对序列的顺序进行排列,然后对中间序列进行遍历,对每个
y
y
y求出答案取最小值,值得思考的是怎么优化代码长度,这里推荐使用指针参数或者数组参数缩短代码
时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
代码实现
#include<bits/stdc++.h>
#define INF 9223372036854775807LL
#define int long long
#define MAXN 300005
#define mod 1000000007
#define PI 3.14
#define eps 1e-10
#define pa pair<int,int>
#define ms(a,x) memset(a,x,sizeof(a))
#define mc(ar1,ar2) memcpy(ar1,ar2,sizeof(ar2))
#define mkp(a,b) make_pair(a,b)
#define ls (p<<1)
#define rs (p<<1|1)
#define fn(i,st,ed) for(int i=st;i<=ed;++i)
#define fd(i,st,ed) for(int i=st;i>=ed;--i)
#define fg(i,x,head,e) for(int i=head[x];~i;i=e[i].nxt)
using namespace std;
inline int read(){int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;}
bool cmp(int x,int y){return x<y;}
int r[MAXN],g[MAXN],b[MAXN],ans;
int val(int x,int y,int z){return (x-y)*(x-y)+(x-z)*(x-z)+(y-z)*(y-z);}
int find(int *rr,int *gg,int *bb,int nrr,int ngg,int nbb){
int res=INF,l=0,r=0;
fn(i,1,nrr){
while(l<=ngg&&gg[l]<=rr[i])l++;
while(r<=nbb&&bb[r]<rr[i])r++;
if(l!=1&&r!=nbb+1)res=min(res,val(rr[i],gg[l-1],bb[r]));
}
return res;
}
void solve(){
ans=INF;
int nr,ng,nb;cin>>nr>>ng>>nb;
fn(i,1,nr)cin>>r[i];sort(r+1,r+nr+1,cmp);
fn(i,1,ng)cin>>g[i];sort(g+1,g+ng+1,cmp);
fn(i,1,nb)cin>>b[i];sort(b+1,b+nb+1,cmp);
ans=min(ans,find(r,g,b,nr,ng,nb));
ans=min(ans,find(r,b,g,nr,nb,ng));
ans=min(ans,find(g,r,b,ng,nr,nb));
ans=min(ans,find(g,b,r,ng,nb,nr));
ans=min(ans,find(b,r,g,nb,nr,ng));
ans=min(ans,find(b,g,r,nb,ng,nr));
cout<<ans<<endl;
return;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--){solve();}
return 0;
}
L Floor and Mod(1700)
原题链接:https://codeforces.com/problemset/problem/1485/C
题目大意
给定 x x x, y y y,问满足 1 ≤ a ≤ x 1\leq a\leq x 1≤a≤x, 1 ≤ b ≤ y 1\leq b\leq y 1≤b≤y, ⌊ a b ⌋ = a m o d b \left\lfloor\dfrac{a}{b}\right\rfloor=a \bmod b ⌊ba⌋=amodb的 ( a , b ) (a,b) (a,b)对数
题解
本题解法很多,这里只叙述我的做法,根据
⌊
a
b
⌋
=
a
m
o
d
b
\left\lfloor\dfrac{a}{b}\right\rfloor=a \bmod b
⌊ba⌋=amodb可知,若设
k
=
⌊
a
b
⌋
=
a
m
o
d
b
k=\left\lfloor\dfrac{a}{b}\right\rfloor=a \bmod b
k=⌊ba⌋=amodb,则有
a
=
k
b
+
k
a=kb+k
a=kb+k,且
k
≤
a
k\leq \sqrt{a}
k≤a,所以可以枚举
k
k
k,对于每个
k
k
k答案就是与此时对应的最大对数,即
a
a
a和
b
b
b的最小值再去掉
k
k
k对,用数学语言表达就是
a
n
s
=
Σ
k
=
1
a
m
a
x
(
0
,
m
i
n
(
x
−
k
k
,
y
)
−
k
)
ans=\Sigma_{k=1}^{\sqrt{a}}max(0,min(\frac{x-k}{k},y)-k)
ans=Σk=1amax(0,min(kx−k,y)−k)
时间复杂度
O
(
a
)
O(\sqrt{a})
O(a)
代码实现
#include<bits/stdc++.h>
#define INF 2147483647LL
#define int long long
#define MAXN 300005
#define mod 1000000007
#define PI 3.14
#define eps 1e-10
#define pa pair<int,int>
#define ms(a,x) memset(a,x,sizeof(a))
#define mc(ar1,ar2) memcpy(ar1,ar2,sizeof(ar2))
#define mkp(a,b) make_pair(a,b)
#define ls (p<<1)
#define rs (p<<1|1)
#define fn(i,st,ed) for(int i=st;i<=ed;++i)
#define fd(i,st,ed) for(int i=st;i>=ed;--i)
#define fg(i,x,head,e) for(int i=head[x];~i;i=e[i].nxt)
using namespace std;
inline int read(){int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;}
void solve(){
int x,y,ans=0;cin>>x>>y;
fn(i,1,sqrt(x))ans+=max(0LL,min(y,(x-i)/i)-i);
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--){solve();}
return 0;
}
M Divide and Summarize(1600)
原题链接:https://codeforces.com/problemset/problem/1461/D
题目大意
给定一长度为
n
n
n的序列
a
a
a,进行以下操作:
1.
1.
1.找到序列最大值
M
M
M和最小值
m
m
m,记
m
i
d
=
⌊
M
+
m
2
⌋
mid=\left\lfloor\dfrac{M+m}{2}\right\rfloor
mid=⌊2M+m⌋
2.
2.
2.将原序列中小于等于
m
i
d
mid
mid的元素放入左序列,,大于
m
i
d
mid
mid的题放入右序列,任意舍弃其中之一,并对留下的序列继续进行上述操作
询问对于每个输入的
x
x
x,是否在上述操作中有可能有序列和为
x
x
x
题解
不难发现,题目操作实际是在模拟快排的过程,但是题目解法和快排原理关系不大,可以发现,
1
≤
a
i
≤
1
0
6
1\leq a_i\leq 10^6
1≤ai≤106,因此序列数量最多不会超过
1
0
6
l
o
g
(
1
0
6
)
10^6log(10^6)
106log(106)个,并且递归次数不会超过
l
o
g
(
1
0
5
)
log(10^5)
log(105)次,所以从时间和空间角度不会超时或者爆空间,那么我们只需要模拟题目中的操作分治,每次对当前序列求和并记入map或set等数据结构,最后对每次询问进行判断即可
时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
代码实现
#include<bits/stdc++.h>
#define INF 2147483647LL
#define int long long
#define MAXN 300005
#define mod 1000000007
#define PI 3.14
#define eps 1e-10
#define pa pair<int,int>
#define ms(a,x) memset(a,x,sizeof(a))
#define mc(ar1,ar2) memcpy(ar1,ar2,sizeof(ar2))
#define mkp(a,b) make_pair(a,b)
#define ls (p<<1)
#define rs (p<<1|1)
#define fn(i,st,ed) for(int i=st;i<=ed;++i)
#define fd(i,st,ed) for(int i=st;i>=ed;--i)
#define fg(i,x,head,e) for(int i=head[x];~i;i=e[i].nxt)
using namespace std;
inline int read(){int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;}
int a[MAXN],sum[MAXN];
set<int>ans;
void dfs(int l,int r){
if(l>r)return;
ans.insert(sum[r]-sum[l-1]);
int mid=a[r]+a[l]>>1;
int id=upper_bound(a+l,a+r+1,mid)-a;
if(id>r)return;
dfs(l,id-1);
dfs(id,r);
}
void solve(){
int n,q;cin>>n>>q;ans.clear();
fn(i,1,n)cin>>a[i];
sort(a+1,a+1+n);
fn(i,1,n)sum[i]=sum[i-1]+a[i];
ans.insert(sum[n]);
dfs(1,n);
while(q--){
int x;cin>>x;
if(*ans.lower_bound(x)==x)cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--){solve();}
return 0;
}
D Linova and Kingdom(1600)
原题链接:https://codeforces.com/problemset/problem/1336/A
题目大意
给定一棵有 n n n个点的树,标记树上任意 k k k个点,价值为每个被标记点到根路径上标记点数量之和,求价值的最大值
题解
对于树上某点,到达根的路径唯一,不难想出,若一个点被选,则他子树上的所有点都会被选,以下为证明
若某点被选,但子树中有点未被选,因为子树中点的深度一定比字数的根大,因此选择子树中的点不可能比选择子树的根更劣,所以若一个点被选,那么他子树上所有点都已经被选
得出该结论后不难得出对于一个点,若选择这个点,则会产生的 d e p i dep_i depi的贡献,但是由于会侵占子树一个为标记点,所以子树每个点贡献 − 1 -1 −1,所以对于一个点 i i i,其产生的贡献就是 d e p i − s i z i dep_i-siz_i depi−sizi,将得到的所有答案存入堆,取前k个求和即可
代码实现
#include<bits/stdc++.h>
#define INF 2147483647LL
#define int long long
#define MAXN 300005
#define mod 1000000007
#define PI 3.14
#define eps 1e-10
#define pa pair<int,int>
#define ms(a,x) memset(a,x,sizeof(a))
#define mc(ar1,ar2) memcpy(ar1,ar2,sizeof(ar2))
#define mkp(a,b) make_pair(a,b)
#define ls (p<<1)
#define rs (p<<1|1)
#define fn(i,st,ed) for(int i=st;i<=ed;++i)
#define fd(i,st,ed) for(int i=st;i>=ed;--i)
#define fg(i,x,head,e) for(int i=head[x];~i;i=e[i].nxt)
using namespace std;
inline int read(){int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;}
struct edge{int to,nxt;}e[MAXN<<1];
int head[MAXN],ecnt=-1,dep[MAXN],siz[MAXN];
void insert(int x,int y){e[++ecnt]={y,head[x]};head[x]=ecnt;}
priority_queue<int,vector<int>,less<int> >heap;
void dfs(int x,int fa){
dep[x]=dep[fa]+1,siz[x]=1;
fg(i,x,head,e){
int to=e[i].to;
if(to==fa)continue;
dfs(to,x);
siz[x]+=siz[to];
}
heap.push(dep[x]-siz[x]);
}
void solve(){
ms(head,-1);
int n,k,ans=0;cin>>n>>k;
fn(i,1,n-1){
int x,y;cin>>x>>y;
insert(x,y);insert(y,x);
}
dfs(1,0);
fn(i,1,k){ans+=heap.top();heap.pop();}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T=1;
while(T--){solve();}
return 0;
}
G Permutation Restoration(1900)
原题链接:https://codeforces.com/problemset/problem/1701/D
题目大意
给定序列长度为 n n n的 b i b_i bi,要求还原原排列 a i a_i ai,满足 b i = ⌊ i a i ⌋ b_i=\left\lfloor\dfrac{i}{a_i}\right\rfloor bi=⌊aii⌋
题解
由题目中所给式子,可以推出
a
i
a_i
ai的取值范围,即
b
i
≤
i
a
i
b_i\leq \frac{i}{a_i}
bi≤aii
⇒
a
i
≤
i
b
i
\Rightarrow a_i\leq\frac{i}{b_i}
⇒ai≤bii
b
i
+
1
>
i
a
i
b_i+1>\frac{i}{a_i}
bi+1>aii
⇒
a
i
>
i
b
i
+
1
\Rightarrow a_i>\frac{i}{b_i+1}
⇒ai>bi+1i
∴
i
b
i
+
1
<
a
i
≤
i
b
i
\therefore \frac{i}{b_i+1}<a_i\leq \frac{i}{b_i}
∴bi+1i<ai≤bii
由此,题目转换为尝试求出不相同且满足所有
a
i
a_i
ai条件的
a
i
a_i
ai,不难发现,题目已经转换为经典的线段覆盖问题,我们可以对左端点进行排序,然后按照右端点从小到大塞入堆中,依次取出合法
a
i
a_i
ai即可
代码实现
#include<bits/stdc++.h>
#define INF 2147483647LL
#define int long long
#define MAXN 500005
#define mod 1000000007
#define PI 3.14
#define eps 1e-10
#define pa pair<int,int>
#define ms(a,x) memset(a,x,sizeof(a))
#define mc(ar1,ar2) memcpy(ar1,ar2,sizeof(ar2))
#define mkp(a,b) make_pair(a,b)
#define ls (p<<1)
#define rs (p<<1|1)
#define fn(i,st,ed) for(int i=st;i<=ed;++i)
#define fd(i,st,ed) for(int i=st;i>=ed;--i)
#define fg(i,x,head,e) for(int i=head[x];~i;i=e[i].nxt)
using namespace std;
inline int read(){int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;}
struct seg{int l,r,id;}s[MAXN];
bool cmp(seg a,seg b){return a.l<b.l;}
int a[MAXN],ans[MAXN];
void solve(){
priority_queue<pa,vector<pa>,greater<pa> >heap;
int n,num=1;cin>>n;
fn(i,1,n){
cin>>a[i];
s[i]={i/(a[i]+1)+1,a[i]==0?(n+1):i/a[i],i};
}
sort(s+1,s+n+1,cmp);
fn(i,1,n){
while(num<=n&&s[num].l==i){
heap.push({(s[num].r==n+1)?n:s[num].r,s[num].id});
num++;
}
ans[heap.top().second]=i;
heap.pop();
}
fn(i,1,n)cout<<ans[i]<<' ';cout<<endl;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--){solve();}
return 0;
}
A Card Game(1500)
原题链接:https://codeforces.com/problemset/problem/1739/C
题目大意
游戏牌堆中含偶数 n n n张牌,每张牌上的数字不同,且大小在 1 1 1到 n n n之间(即给定 1 1 1到 n n n的全排列牌,又称 permutation)。两名玩家 A、B 都会在开局分得牌堆中的 n 2 \frac{n}{2} 2n张牌,手牌互异,点数随机。 首先玩家 A 出牌,对手 B 应牌(对手应牌点数需比出牌者大,且丢弃),然后玩家 B 出牌,对手 A 应牌,依次轮转,直至一人无法应牌判定其为输(无更大牌可应),或双方手牌为空判定平局(双方皆空手无牌可出)。 给定牌的张数 n n n,求能使 A 获胜的发牌方式数、能使 B 获胜的发牌方式数、能使双方平局的发牌方式数。
题解
首先考虑平局方案,若是A拿到点数为
n
n
n的牌,则A可以打出该牌并直接获得胜利,所以B必须获得
n
n
n,而如果B同时获得了点数为
n
−
1
n-1
n−1的牌,则B此时打出该牌会直接获得胜利,因此A必须获得
n
−
1
n-1
n−1,以此类推,平局方案只有一种,两人交错获得
然后考虑先手必胜态从
i
−
2
i-2
i−2张牌向
i
i
i张牌转移的方案转换,当这样转移的时候牌堆中会多两张牌
i
−
1
i-1
i−1和
i
i
i,讨论A和B对牌的获得情况
若A同时获得
i
−
1
i-1
i−1和
i
i
i,则可以直接打出
i
i
i获得胜利,转移方案数为
C
i
i
2
−
2
C_{i}^{\frac{i}{2}-2}
Ci2i−2
若A获得
i
i
i,B获得
i
−
1
i-1
i−1,A仍然可以直接打出
i
i
i获得胜利,转移方案为
C
i
i
2
−
1
C_{i}^{\frac{i}{2}-1}
Ci2i−1
若A获得
i
−
1
i-1
i−1,B获得
i
i
i,则A打出
i
−
1
i-1
i−1,B打出
i
i
i应牌,此时状态转移为有
i
−
2
i-2
i−2张牌时的后手必胜态
若B获得两张牌,则此时A必败
综上所述,先手必胜态转移方程如下
a
i
=
C
i
i
2
−
2
+
C
i
i
2
−
1
+
b
i
−
2
a_i=C_{i}^{\frac{i}{2}-2}+C_{i}^{\frac{i}{2}-1}+b_{i-2}
ai=Ci2i−2+Ci2i−1+bi−2
再考虑后手必胜态,因为发牌总方案数为
C
i
i
2
C_{i}^{\frac{i}{2}}
Ci2i种,所以后手必胜态转移方程就是
b
i
=
C
i
i
2
−
a
i
−
1
b_i=C_{i}^{\frac{i}{2}}-a_i-1
bi=Ci2i−ai−1
代码实现
#include<bits/stdc++.h>
#define INF 2147483647LL
#define int long long
#define MAXN 300005
#define mod 998244353
#define PI 3.14
#define eps 1e-10
#define pa pair<int,int>
#define ms(a,x) memset(a,x,sizeof(a))
#define mc(ar1,ar2) memcpy(ar1,ar2,sizeof(ar2))
#define mkp(a,b) make_pair(a,b)
#define ls (p<<1)
#define rs (p<<1|1)
#define fn(i,st,ed) for(int i=st;i<=ed;++i)
#define fd(i,st,ed) for(int i=st;i>=ed;--i)
#define fg(i,x,head,e) for(int i=head[x];~i;i=e[i].nxt)
using namespace std;
//inline int read(){int =0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;}
int a[MAXN],b[MAXN],d[MAXN],c[105][105];
void init(){
fn(i,0,60){
c[i][0]=c[i][i]=1;
fn(j,1,i-1)c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
a[2]=1,b[2]=0,d[2]=1;
for(int i=4;i<=60;i+=2){
d[i]=1;
a[i]=c[i-2][(i>>1)-2]+c[i-2][(i-2)>>1]+b[i-2];
a[i]%=mod;
b[i]=mod+c[i][i>>1]-a[i]-1;
b[i]%=mod;
}
}
void solve(){
int n;cin>>n;
cout<<a[n]<<' '<<b[n]<<' '<<d[n]<<endl;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
init();
int T;cin>>T;
while(T--){solve();}
return 0;
}
H Maximum AND(1800)
原题链接:https://codeforces.com/problemset/problem/1721/D
题目大意
给定两个序列
a
a
a和
b
b
b,定义
f
(
a
,
b
)
f(a,b)
f(a,b)为
1.
1.
1.对所有
1
≤
i
≤
n
1\leq i \leq n
1≤i≤n,求
c
i
=
a
i
⊕
b
i
c_i=a_i \oplus b_i
ci=ai⊕bi(
⊕
\oplus
⊕为按位异或)
2.
2.
2.对所有
1
≤
i
≤
n
1\leq i \leq n
1≤i≤n,求与和,即
f
(
a
,
b
)
=
c
1
&
c
2
&
c
3
.
.
.
&
c
n
f(a,b)=c_1\&c_2\&c_3...\&c_n
f(a,b)=c1&c2&c3...&cn
对于
b
i
b_i
bi的任意排列,求最大的
f
(
a
,
b
)
f(a,b)
f(a,b)
题解
对于
f
(
a
,
b
)
f(a,b)
f(a,b)的二进制分解,若想让某一位是
1
1
1,则需要所有
c
i
c_i
ci的该位都是
1
1
1,即所有
a
i
a_i
ai和
b
i
b_i
bi在该位上都是不同的,而对于已经确定的某位是
1
1
1,需要确保后续交换顺序时该位数字和原来相同
所以对于已经确定的某位是
1
1
1,我们可以对所有
a
i
a_i
ai和
b
i
b_i
bi按照该位上的
(
0
/
1
)
(0/1)
(0/1)分开,递归进入子集,若子集合答案都能取到,则整体答案能取到
代码实现
#include<bits/stdc++.h>
#define INF 2147483647LL
#define int long long
#define MAXN 300005
#define mod 1000000007
#define PI 3.14
#define eps 1e-10
#define pa pair<int,int>
#define ms(a,x) memset(a,x,sizeof(a))
#define mc(ar1,ar2) memcpy(ar1,ar2,sizeof(ar2))
#define mkp(a,b) make_pair(a,b)
#define ls (p<<1)
#define rs (p<<1|1)
#define fn(i,st,ed) for(int i=st;i<=ed;++i)
#define fd(i,st,ed) for(int i=st;i>=ed;--i)
#define fg(i,x,head,e) for(int i=head[x];~i;i=e[i].nxt)
using namespace std;
inline int read(){int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f;}
bool cmp(int x,int y){return x<y;}
bool cmp1(int x,int y){return x>y;}
int a[MAXN],b[MAXN];
void solve(){
int n,ans=0;cin>>n;
fn(i,1,n)cin>>a[i];
fn(i,1,n)cin>>b[i];
sort(a+1,a+n+1,cmp);
sort(b+1,b+n+1,cmp1);
fd(i,30,0){
int tmp=n+1;
fn(j,1,n)if((a[j]&(1<<i))==(b[j]&(1<<i))){tmp=j;break;}
if(tmp>n)ans|=(1<<i);
else {
fn(j,1,n)a[j]|=(1<<i),b[j]|=(1<<i);
sort(a+1,a+n+1,cmp);
sort(b+1,b+n+1,cmp1);
}
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--){solve();}
return 0;
}