A. Array Coloring
题意:
给定一个长度为
n
n
n 的正整数数组,以两种颜色为其所有元素上色
问: 能否使两种颜色的元素的和具有相同的奇偶性,并且每种颜色至少有一个元素上色。
思路:
对于一个和
s
u
m
sum
sum :
- 将一个偶数加进去,不会改变其奇偶性
- 将一个奇数加进去,它的奇偶性会改变
因此如果两种颜色和的奇偶性相同,那么两种颜色里包含的奇数数量奇偶性应该相同
→
\rightarrow
→ 数组
a
a
a 中奇数的数量一定是偶数
// Problem: Array Coloring
// Contest: Codeforces
// URL: https://m1.codeforces.com/contest/1857/problem/A
// Memory Limit: 256 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
inline int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return f*x;
}
int main(){
int t=read();
while(t--){
int n=read();
int cnt=0;
fore(i,1,n+1){
int x=read();
cnt+=(x%2==1);
}
std::cout<<(cnt%2==0?"YES":"NO")<<endl;
}
return 0;
}
B. Maximum Rounding
题意:
给定一个整数
x
x
x
定义一个类似四舍五入的操作:
- 如果这一位大于等于 5 5 5 ,往更高位进 1 1 1,并且这一位以及后面所有的位都变成 0 0 0
这个操作可以执行任意次,求出最终最大的结果
思路:
直接从低位往高位模拟,遇到一个大于等于
5
5
5 的位,就往前进
1
1
1 (特判一些这一位是不是最高位)
然后给这位打上记号,输出的时候遇到这个记号就表示后面全是
0
0
0
// Problem: Maximum Rounding
// Contest: Codeforces
// URL: https://m1.codeforces.com/contest/1857/problem/B
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
inline int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return f*x;
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin>>t;
while(t--){
std::string s;
std::cin>>s;
int idx=s.size()-1;
while(idx>=0){
if(s[idx]<'5'){
--idx;
continue;
}
s[idx]='p'; //记号
if(idx>0) ++s[idx-1]; //不是最高位,还可以往前进位
else std::cout<<1; //最高位
--idx;
}
fore(i,0,s.size())
if(s[i]!='p') std::cout<<s[i];
else{
fore(j,i,s.size()) std::cout<<0;
break;
}
std::cout<<endl;
}
return 0;
}
C. Assembly via Minimums
题意:
有一个长度为
n
n
n 的数组
a
a
a
定义长度为
n
∗
(
n
−
1
)
2
\dfrac {n*(n-1)}{2}
2n∗(n−1) 数组
b
b
b :
- ∀ i , j ∈ [ 1 , n ] , m i n ( a i , a j ) ∈ b \forall i,j \in [1,n] ,\quad min(a_i,a_j) \in b ∀i,j∈[1,n],min(ai,aj)∈b
也就是
b
b
b 包含了
a
a
a 所有配对的最小值
现在给定
b
b
b,构造出
a
a
a
思路:
假设数组
a
a
a 非递减:
a
1
≤
a
2
≤
.
.
.
≤
a
n
a_1 \leq a_2 \leq ... \leq a_n
a1≤a2≤...≤an
那么
b
b
b 中:
a
1
a_1
a1 有
n
−
1
n-1
n−1 个,
a
2
a_2
a2 有
n
−
2
n-2
n−2 个…
a
i
a_i
ai 有
n
−
i
n-i
n−i 个
可以先排序
b
b
b ,从小到大根据
b
b
b 中的元素确定
a
a
a 中的元素
对于
b
b
b 中第一小的元素,排在
a
1
a_1
a1,然后索引
i
i
i 要跳过这
n
−
i
n-i
n−i 个元素(因为他们都是相同的),寻找下一个更大一点的元素
// Problem: C. Assembly via Minimums
// Contest: Codeforces - Codeforces Round 891 (Div. 3)
// URL: https://codeforces.com/contest/1857/problem/C
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
inline int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return f*x;
}
int main(){
int t=read();
while(t--){
int n=read();
int m=n*(n-1)/2;
std::vector<int> b(m);
fore(i,0,m) b[i]=read();
std::sort(b.begin(),b.end());
for(int i=0;i<m;i+=--n)
std::cout<<b[i]<<' ';
std::cout<<b[m-1]<<endl;
}
return 0;
}
D. Strong Vertices
题意:
给定长度为
n
n
n 的两个数组
a
a
a 和
b
b
b
并定义一个有向图的边:
- 如果 a u − a v ≥ b u − b v ( u ≠ v ) a_u - a_v \geq b_u - b_v (u \neq v) au−av≥bu−bv(u=v) ,那么存在 u − > v u -> v u−>v 的边
定义一个 强点 为:
- 可以到达其他所有点的点
要求找出图中所有的 强点
思路:
如果
u
−
>
v
(
u
≠
v
)
u -> v (u \neq v)
u−>v(u=v),那么
a
u
−
a
v
≥
b
u
−
b
v
a_u - a_v \geq b_u - b_v
au−av≥bu−bv,即
a
u
−
b
u
≥
a
v
−
b
v
a_u - b_u \geq a_v - b_v
au−bu≥av−bv
记 c i = a i − b i c_i = a_i - b_i ci=ai−bi,那么当且仅当 c u ≥ c v c_u \geq c_v cu≥cv 时, u − > v u -> v u−>v
如果一个点是 强点,那么它对应的
c
i
c_i
ci 一定是 最大 的!
统计
c
i
c_i
ci 的最大值对应的那些点即可
// Problem: D. Strong Vertices
// Contest: Codeforces - Codeforces Round 891 (Div. 3)
// URL: https://codeforces.com/contest/1857/problem/D
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return f*x;
}
int main(){
int t=read();
while(t--){
int n=read();
std::vector<ll> a(n+1),b(n+1);
fore(i,1,n+1) a[i]=read();
fore(i,1,n+1) b[i]=read();
ll maxv=-INFLL;
fore(i,1,n+1){
a[i]=a[i]-b[i];
if(a[i]>maxv){
maxv=a[i];
}
}
int cnt=0;
fore(i,1,n+1)
if(a[i]==maxv) ++cnt;
std::cout<<cnt<<endl;
fore(i,1,n+1)
if(a[i]==maxv) std::cout<<i<<' ';
std::cout<<endl;
}
return 0;
}
E. Power of Points
题意:
给定
n
n
n 个在一条坐标轴上的点
x
1
.
.
x
n
x_1 .. x_n
x1..xn
对于一个整数
s
s
s ,构造分段区间
[
s
,
x
1
]
,
[
s
,
x
2
]
.
.
.
[s,x_1],[s,x_2]...
[s,x1],[s,x2]... 或
[
x
i
,
s
]
(
x
i
<
s
)
[x_i,s] (x_i<s)
[xi,s](xi<s)
定义一个点的贡献
f
p
f_p
fp:
- f p f_p fp 是与点 p p p 相交的区间数
计算出:
∑
p
=
1
1
0
9
f
p
∀
s
∈
x
1
,
x
2
,
.
.
x
n
\sum_{p=1}^{10^9} f_p \quad \forall s \in {x_1,x_2,..x_n}
∑p=1109fp∀s∈x1,x2,..xn
思路:
对于一个整数
s
s
s 形成的分段区间
[
s
,
x
1
]
,
[
s
,
x
2
]
.
.
.
[s,x_1],[s,x_2]...
[s,x1],[s,x2]... 或
[
x
i
,
s
]
(
x
i
<
s
)
[x_i,s] (x_i<s)
[xi,s](xi<s),
∑
f
p
=
所有区间长度之和
\sum f_p =所有区间长度之和
∑fp=所有区间长度之和!
可以先对
x
i
x_i
xi 排序, 当
s
=
x
i
s=x_i
s=xi 时,答案就是:
∑
j
=
1
i
(
s
−
x
j
+
1
)
+
∑
j
=
i
+
1
n
(
x
j
−
s
+
1
)
\sum_{j=1}^{i}(s-x_j+1) + \sum_{j=i+1}^{n}(x_j-s+1)
∑j=1i(s−xj+1)+∑j=i+1n(xj−s+1)
=
n
+
i
×
s
−
∑
j
=
1
i
x
j
+
∑
j
=
i
+
1
n
x
j
−
(
n
−
i
)
×
s
=n + i\times s -\sum_{j=1}^{i}x_j +\sum_{j=i+1}^{n}x_j - (n-i)\times s
=n+i×s−∑j=1ixj+∑j=i+1nxj−(n−i)×s
=
n
+
(
n
−
2
i
)
×
s
−
∑
j
=
1
i
x
j
+
∑
j
=
i
+
1
n
x
j
=n + (n-2i)\times s -\sum_{j=1}^{i}x_j +\sum_{j=i+1}^{n}x_j
=n+(n−2i)×s−∑j=1ixj+∑j=i+1nxj
分别用前缀和与后缀和 维护一下就可以
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
inline int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return f*x;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
std::vector<std::pair<ll,int>> a(n+1); //值和编号
fore(i,1,n+1){
scanf("%lld",&a[i].fi);
a[i].se=i;
}
std::sort(a.begin(),a.end());
std::vector<ll> pre(n+5),suf(n+5); //前缀和 后缀和
fore(i,1,n+1) pre[i]=pre[i-1]+a[i].fi;
for(int i=n;i>=1;--i) suf[i]=suf[i+1]+a[i].fi;
std::vector<ll> ans(n+5);
fore(i,1,n+1) ans[a[i].se]=n+a[i].fi*(2ll*i-n)-pre[i]+suf[i+1];
fore(i,1,n+1) std::cout<<ans[i]<<' ';
std::cout<<endl;
}
return 0;
}
F. Sum and Product
题意:
给定一个长度为
n
n
n 的数组
a
a
a
对于
q
q
q 个询问
x
、
y
x、y
x、y ,回答有多少个配对满足:
- 1 ≤ i < j ≤ n 1 \leq i < j \leq n 1≤i<j≤n
- a i + a j = x a i ⋅ a j = y a_i + a_j =x \quad a_i \cdot a_j = y ai+aj=xai⋅aj=y
思路:
令
x
=
b
,
y
=
c
x=b,y=c
x=b,y=c,则
a
i
和
a
j
a_i 和 a_j
ai和aj 可以看成是方程
x
2
−
b
x
+
c
=
0
x^2 -bx + c=0
x2−bx+c=0 的两个根(韦达定理)
解出
x
1
和
x
2
x_1 和 x_2
x1和x2,根据它们在数组中出现的次数做配对即可
令 δ = b 2 − 4 c \delta = b^2 - 4c δ=b2−4c
- 若 δ < 0 \delta < 0 δ<0 ,无解
- 若 δ = 0 \delta = 0 δ=0, x 1 = x 2 x_1 = x_2 x1=x2,此时两根相同,取两个出现位置即可, C c n t [ x ] 2 C_{cnt[x]}^2 Ccnt[x]2
- 若 δ > 0 \delta > 0 δ>0, 就是两根出现次数的乘积: c n t [ x 1 ] × c n t [ x 2 ] cnt[x_1] \times cnt[x_2] cnt[x1]×cnt[x2]
// Problem: F. Sum and Product
// Contest: Codeforces - Codeforces Round 891 (Div. 3)
// URL: https://codeforces.com/contest/1857/problem/F
// Memory Limit: 256 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
inline int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return f*x;
}
std::map<ll,int> cnt;
ll solve(ll b,ll c){
ll D=b*b-4ll*c;
if(D<0) return 0;
ll x1=(b-sqrt(D))/2;
ll x2=(b+sqrt(D))/2;
if(x1+x2 != b || x1*x2!=c) return 0;
if(x1==x2) return 1ll*cnt[x1]*(cnt[x1]-1)/2;
return 1ll*cnt[x1]*cnt[x2];
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int t;
std::cin>>t;
while(t--){
cnt.clear();
int n;
std::cin>>n;
fore(i,0,n){
ll x;
std::cin>>x;
++cnt[x];
}
int q;
std::cin>>q;
while(q--){
ll b,c;
std::cin>>b>>c;
std::cout<<solve(b,c)<<" \n"[q==0];
}
}
return 0;
}
G. Counting Graphs
题意:
有一颗
n
n
n 个节点的树,每条边有边权
w
i
w_i
wi
给定
S
S
S,求出有多少个不同的图,满足以下条件:
- 无重边和自环
- 边权是整数且不超过 S ( w i ≤ S ) S \quad (w_i \leq S) S(wi≤S)
- 有且仅有唯一一个最小生成树是原先 给定的树
思路:
这道题目出得很好,涉及的一些基础的算法很广:最小生成树、快速幂、并查集(记录连通块节点数)
其实新图就是在原先的树上面添加一些边,定义
P
(
u
,
v
)
P(u,v)
P(u,v) 是树上两点
u
,
v
u ,v
u,v 路径上经过的最大边权
如果要添加一条边连接
u
v
u \hspace{5pt} v
uv,那么这条边的边权
W
∈
[
P
(
u
,
v
)
+
1
,
S
]
W \in [P(u,v)+1,S]
W∈[P(u,v)+1,S],如果
W
≤
P
(
u
,
v
)
W \leq P(u,v)
W≤P(u,v),生成的
M
S
T
MST
MST 就不是原来的树了
如果连接
u
v
u \hspace{5pt} v
uv,那么边权
W
W
W 有
S
−
P
(
u
,
v
)
S-P(u,v)
S−P(u,v) 种可能,如果不连,有
1
1
1 种可能
相当于每两个没有直接边连起来的点
u
v
u \hspace{5pt} v
uv,都有
(
S
−
P
(
u
,
v
)
+
1
)
(S-P(u,v)+1)
(S−P(u,v)+1) 种可能性
那么答案就是:
∏
1
≤
u
<
v
≤
n
(
S
−
P
(
u
,
v
)
+
1
)
\prod_{1\leq u < v \leq n}(S-P(u,v)+1)
∏1≤u<v≤n(S−P(u,v)+1)
考虑类似
K
r
u
s
k
a
l
Kruskal
Kruskal 的方式,将树上边权排序 :
w
1
≤
w
2
≤
.
.
.
≤
w
n
−
1
w_1 \leq w_2 \leq ...\leq w_{n-1}
w1≤w2≤...≤wn−1
由于是一棵树,所以所有边都是有用的,不会存在丢弃边的情况
假设已经合并了前
i
−
1
i-1
i−1 个边的节点,现在对于第
i
i
i 条边
(
u
i
,
v
i
,
w
i
)
(u_i,v_i,w_i)
(ui,vi,wi),它的边权
w
i
w_i
wi 一定大于等于前面的边权
此时
u
i
v
i
u_i \hspace{5pt} v_i
uivi 来自不同的连通块,从一个连通块的点到另一个连通块的点,经过的最大边权就是
w
i
w_i
wi
u i u_i ui 连通块里面的点数 s i z e 1 size_1 size1 和 v i v_i vi 连通块里面的点数 s i z e 2 size_2 size2,那么就有 s i z e 1 × s i z e 2 − 1 size_1 \times size_2 -1 size1×size2−1 个配对( u i v i u_i \hspace{5pt} v_i uivi 之间的边不算),它们路径经过的最大边权是 w i w_i wi
因此,第 i i i 条边的可能性有 ( S − w i + 1 ) s i z e 1 × s i z e 2 − 1 (S-w_i+1)^{size_1 \times size_2 -1} (S−wi+1)size1×size2−1,把所有可能性乘起来就是答案
// Problem: G. Counting Graphs
// Contest: Codeforces - Codeforces Round 891 (Div. 3)
// URL: https://codeforces.com/contest/1857/problem/G
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
const int INF=0x3f3f3f3f;
const long long INFLL=0x3f3f3f3f3f3f3f3fLL;
typedef long long ll;
inline int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return f*x;
}
const ll mod=998244353;
const int N=200050;
int fa[N];
ll size[N]; //每个连通块的点的数量
struct Edge{
int u;
int v;
int w;
}edge[N];
bool cmp(const Edge& a,const Edge& b){
return a.w<b.w;
}
int find(int x){
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
ll fast_pow(ll a,ll b,ll mod){
ll res=1;
a%=mod;
b%=(mod-1); //这里可以用欧拉定理取下模
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,S;
ll ans=1;
scanf("%d %d",&n,&S);
fore(i,1,n) scanf("%d %d %d",&edge[i].u,&edge[i].v,&edge[i].w);
fore(i,1,n+1){
fa[i]=i;
size[i]=1;
}
std::sort(edge+1,edge+n,cmp); //边的数量是n-1
fore(i,1,n){
int u=edge[i].u,v=edge[i].v,w=edge[i].w;
/* 不会有丢弃边的情况 因为是一颗树 每一条边都要用上 */
int fau=find(u),fav=find(v);
ans=ans*fast_pow(S-w+1,size[fau]*size[fav]-1,mod)%mod;
fa[fau]=fav;
size[fav]+=size[fau]; //合并连通块 点的数量增加
}
printf("%lld\n",ans);
}
return 0;
}