Codeforces Round 891 div3 A-G 个人练习

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(n1) 数组 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 a1a2...an
那么 b b b 中: a 1 a_1 a1 n − 1 n-1 n1 个, a 2 a_2 a2 n − 2 n-2 n2 个… a i a_i ai n − i n-i ni
可以先排序 b b b ,从小到大根据 b b b 中的元素确定 a a a 中的元素
对于 b b b 中第一小的元素,排在 a 1 a_1 a1,然后索引 i i i 要跳过这 n − i n-i ni 个元素(因为他们都是相同的),寻找下一个更大一点的元素

// 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) auavbubv(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 auavbubv,即 a u − b u ≥ a v − b v a_u - b_u \geq a_v - b_v aubuavbv

c i = a i − b i c_i = a_i - b_i ci=aibi,那么当且仅当 c u ≥ c v c_u \geq c_v cucv 时, 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=1109fpsx1,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(sxj+1)+j=i+1n(xjs+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×sj=1ixj+j=i+1nxj(ni)×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+(n2i)×sj=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 xy ,回答有多少个配对满足:

  • 1 ≤ i < j ≤ n 1 \leq i < j \leq n 1i<jn
  • a i + a j = x a i ⋅ a j = y a_i + a_j =x \quad a_i \cdot a_j = y ai+aj=xaiaj=y

思路:
x = b , y = c x=b,y=c x=by=c,则 a i 和 a j a_i 和 a_j aiaj 可以看成是方程 x 2 − b x + c = 0 x^2 -bx + c=0 x2bx+c=0 的两个根(韦达定理)
解出 x 1 和 x 2 x_1 和 x_2 x1x2,根据它们在数组中出现的次数做配对即可

δ = b 2 − 4 c \delta = b^2 - 4c δ=b24c

  • δ < 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(wiS)
  • 有且仅有唯一一个最小生成树是原先 给定的树

思路:
这道题目出得很好,涉及的一些基础的算法很广:最小生成树、快速幂、并查集(记录连通块节点数)
其实新图就是在原先的树上面添加一些边,定义 P ( u , v ) P(u,v) P(u,v) 是树上两点 u , v u ,v uv 路径上经过的最大边权
如果要添加一条边连接 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) WP(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) SP(u,v) 种可能,如果不连,有 1 1 1 种可能
相当于每两个没有直接边连起来的点 u v u \hspace{5pt} v uv,都有 ( S − P ( u , v ) + 1 ) (S-P(u,v)+1) (SP(u,v)+1) 种可能性
那么答案就是: ∏ 1 ≤ u < v ≤ n ( S − P ( u , v ) + 1 ) \prod_{1\leq u < v \leq n}(S-P(u,v)+1) 1u<vn(SP(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} w1w2...wn1
由于是一棵树,所以所有边都是有用的,不会存在丢弃边的情况
假设已经合并了前 i − 1 i-1 i1 个边的节点,现在对于第 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×size21 个配对( 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} (Swi+1)size1×size21,把所有可能性乘起来就是答案

// 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; 
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Codeforces Round 894 (Div. 3) 是一个Codeforces举办的比赛,是第894轮的Div. 3级别比赛。它包含了一系列目,其中包括目E. Kolya and Movie Theatre。 根据目描述,E. Kolya and Movie Theatre问要求我们给定两个字符串,通过三种操作来让字符串a等于字符串b。这三种操作分别为:交换a中相同位置的字符、交换a中对称位置的字符、交换b中对称位置的字符。我们需要先进行一次预处理,替换a中的字符,然后进行上述三种操作,最终得到a等于b的结果。我们需要计算预处理操作的次数。 根据引用的讨论,当且仅当b[i]==b[n-i-1]时,如果a[i]!=a[n-i-1],需要进行一次操作;否则不需要操作。所以我们可以遍历字符串b的前半部分,判断对应位置的字符是否与后半部分对称,并统计需要进行操作的次数。 以上就是Codeforces Round 894 (Div. 3)的简要说明和目E. Kolya and Movie Theatre的要求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Codeforces Round #498 (Div. 3) (A+B+C+D+E+F)](https://blog.csdn.net/qq_46030630/article/details/108804114)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Codeforces Round 894 (Div. 3)A~E解](https://blog.csdn.net/gyeolhada/article/details/132491891)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值