2023.5.23codeforces训练记录

题目一:1139C - Edgy Trees

大意:

输入 n(2≤n≤1e5) k(2≤k≤100) 和一棵无向树的 n-1 条边(节点编号从 1 开始),每条边包含 3 个数 x y c,表示有一条颜色为 c 的边连接 x 和 y,其中 c 等于 0 或 1。

对于长为 k 节点序列 a,走最短路,按顺序经过节点 a1 -> a2 -> ... -> ak。
对于所有长为 k 的节点序列 a(这有 n^k 个),统计至少经过一条 c=1 的边的序列 a 的个数。

思路:由图可得,总共有n^k个序列,要求至少经过一条 c=1 的边的序列 a 的个数,由容斥定理可得,我们需要求出没有经过c=1的边的序列b的个数。然后n^k 减去该数即可。

由题可得,若两个点之间存在一条c=1的边,那么必然这两个点必然不能计入b的个数中,故可视作c=1的边不会使得两个点连通。故我们可确定若干个由c!=1 连接的连通块。由于序列可重复,故序列b的个数是每个连通块中的节点个数t的  t^k 的和。 计算出b的个数后再用所有结果减去b即可。

代码:

#include <iostream>
#include <cstring>
#include <queue>
#define x first
#define y second 

using namespace std;
typedef long long LL;
const int N =1e5+10 , mod = 1e9 +7 ;

int pre[ N] ;
int v[N] ;
 
int find( int x ){
	if( x == pre[x]) return x; 
	return pre[x] = find( pre[x]  ) ;
}
 
void merge( int a ,int b ){
	int pa = find( a ) , pb = find( b ) ;
	v[pa] += v[pb];
	pre[pb] = pa; 
}
 
LL qmi( LL a , LL b){
	LL res =1;
	while( b ){
		if( b&1) res = ( res *a) % mod;
		b >>=1;
		a = (a*a) %mod;
	}
	return res ;
}

int main()
{
	int T ; T=1;
	while( T --){
		int n ,  k;
		cin >> n  >>  k ;
		for(int i = 1 ; i <= n ; ++i ) pre[i] =  i ,v[i] = 1 ; 
		for(int i = 0 ; i < n-1  ; ++i){
			int a,b,c;
			cin >>a >> b>>c; 
			if( c) continue;
			int pa= find(a )  , pb = find( b ) ;
			if( pa== pb) continue;
			merge( a, b) ;
		}
		LL res = qmi(  n , k ) ; 
		for(int i = 1 ; i <=n; ++i){
			if( pre[i] == i){
				//cout<<res  <<' ' <<qmi( v[i] , k ) <<endl;
				res  = ( res - qmi( v[i] , k) +mod)%mod ;
			}
		}
		cout<< res <<endl;
		
		
	}
	
	
    return 0 ;
}

问题二:1790E - Vlad and a Pair of Numbers

大意:已知 x= a^b = (a+b)/2 ,现给出t个x,若通过x能确定符合条件的a,b则输出,反之输出-1。

思路: 首先我们需要了解一个异或求和公式:

                        a +b =  (a ^ b )  +   (  (a &b ) << 1 )

而由题可得,x =   a ^ b  = (a &b ) << 1 ,故我们可以设 y =  a & b =  x>>1

而因为任何数左移一位后相当于扩大两倍为偶数,故x为奇数时不存在符合条件的a,b。

x为偶数时,  我们可以设a,b初值为0 ,当第i位上, 

x& y = 1 , x^ y = 0 时,显然这个位置上a和b的分配都是1,1   ;

当x&y =1, x^y = 1时,显然不存在符合题意的a,b;

当x&y =0, x^y = 0 时, 显然这个位置上a和b的分配都是0,0;

当x&y =0 , x^y = 1时,显然分别是0,1。

每次分配1时,将1给非0的a,b其中之一。若最终结果存在a或b为0,则不符合条件。

反之,则符合条件。

代码:

#include <iostream>
#include <cstring>
#include <queue>
#define x first
#define y second 

using namespace std;
typedef long long LL;
const int N =1e5+10 , mod = 1e9 +7 ;

int x ;



int main()
{

	int T ; cin >>T;
	while( T --){
		cin >> x ;
		if( x & 1) {
			cout<<"-1" <<endl;continue;
		}
		int y = x>>1;
		bool flag = true;
		int a= 0  , b = 0 ; 
		for(int i = 0 ; i <=29; ++i ){
			int dx = x & ( 1<<i) ,  dy = y & ( 1<<i ) ;
			if( dx && dy) {
				flag= false; break;
			}
			if( !dx && dy){
				a |= 1<< i ; b |= 1<< i ;
			}else if( dx && !dy) {
				if( !a ) a |= 1 <<i;
				else  b |= 1<< i;
			}
		}
		if( !a || !b || !flag ) cout<<-1<<endl;
			else cout<< b <<' ' <<a  << endl;
	}
	
	
    return 0 ;
}

问题三:1790D - Matryoshkas

大意:给定n个长度的数组a, 每次能取出连续的数字。问最少要取多少次。

比如:[2,2,3,4,3,1],取一次,[1,2,3,4],取第二次, [2,3]

思路:存取每个数字存在的次数,每次从存在的数字中取出最小的数,然后找到最大的连续数字后,减少取出的数字次数,并使答案+1。 重复上述操作至不存在数字。

代码:

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#define x first
#define y second 

using namespace std;
typedef long long LL;
const int N =1e5+10 , mod = 1e9 +7 ;

int x ;



int main()
{

	int T ; cin >>T;
	while( T --){
		map<int,int> mp;
		set<int>  s; 
		int n ;cin >> n ;
		for(int i = 0 ; i < n; ++i){
			int x ;cin >>x ;
			if( !mp[x]) s.insert( x ) ;
			mp[x] ++ ;
		}
		int ans = 0 ; 
		while( s.size()) {
			int t = *s.begin() ;
			for(int i = t; mp[i] ; ++i){
				mp[i]--;
				if( !mp[i]) s.erase( i) ;
			}
			++ans  ;
		}
		cout<< ans << endl;
		
	}
	
	
    return 0 ;
}

问题四:1789C - Serval and Toxel's Arrays

大意:有一个长度为n的每个元素都不同的数组a,我将给出m个指令,每个指令有一个pi 和x, 意指将a[ pi]的元素改为x。 设初始数组为A0, 操作1次后的数组为A1, 操作i次的为Ai ...  操作m次后为Am。若将Ai和Aj并在一起(i<j),存在的不同元素的个数记为r(i,j) ,求两两数组之间不同元素的个数之和。操作保证,每次操作后的数组中,每个元素都不同。

1<= ai <=n + m

思路:显然,若每次修改的x都在前面的数组中没有出现过,那结果为 n*m*(m+1).若某个元素总共出现了x次,那么就多算了 x*(x-1)/2次 , 则需要从结果中减去。

代码:

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#define x first
#define y second 

using namespace std;
typedef long long LL;
const int N =4e5+10 , mod = 1e9 +7 ;


int a[N ] ;
LL cnt[N] ;
bool st[N] ;

int main()
{

	int T ; cin >>T;
	while( T --){
		LL n , m ;
		cin >> n >> m ;
		memset( cnt, 0 , sizeof cnt) ;
		for(int i = 1 ; i<=n; ++i) cin >>a[ i ] ;
		for(int i =1;i <=n; ++i) cnt[a[i]] = m+1;
		for(int i=1; i <=m ; ++i){
			int pos , x ;
			cin >> pos >>  x;
			cnt[ a[pos] ] -=  m-i+1;
			cnt[ x] += m-i+1;
			a[pos] = x;
			
		} 
		LL ans = n*m*(m+1);
		for(int i =1;i <=n+m; ++i)
			ans -= cnt[i]*( cnt[i]-1) /2 ;
		cout<< ans <<endl;
		
	}
	
	
    return 0 ;
}

问题五:1788C - Matching Numbers

大意:给定一个n值,请你判断是否有一种2*n的排列p, 使得 p1+p2  = s1 , p3+p4 = s1+1, ...,p2n-1 + p2n = s1+ n-1。  如果有则输出YES和该排列, 否则输出NO。

思路:由等差数列求和等性质可得,s1 = 3*( n+1)/2 。 显然,若n为偶数,则必然不存在。

若n为奇数, 不难发现对于每个i,都有( n*2 - i*2 +1) %n + n +1满足题意。(此处不会推导证明)

代码:

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#define x first
#define y second 

using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int N =4e5+10 , mod = 1e9 +7 ;

int n ;
bool st[N] ;
PII res[N] ;
int main()
{

	int T ; cin >>T;
	while( T --){
		cin >> n ;
		memset( st, false ,sizeof st) ;
		if( (n&1) == 0) {
			cout<< "No" <<endl;continue;
		}else cout<<"Yes"<<endl;
		for(int i =1; i <=n; ++i){
			cout<<i <<" " << ( n*2 - i*2 +1) %n + n +1 <<endl;
		}
		
	}
	
	
    return 0 ;
}

问题六:1786B - Cake Assembly Line

大意:给定n长度数组a,b。再给定w和h。现在能使ai的每个元素都增加或减少任意值,问是否存在某个值,对a的每个元素进行操作后,有 [bi-h,bi+h] 包含于 [ai-w, ai+w]。

题目保证: ai+w < ai+1  - w

思路:由题意得, 每个ai都对应一个bi。我们可以先是将a1的左区间与b1的左区间对准,以增加为正方向,确定可移动的范围[L,R]。之后每考虑ai,则考虑ai,bi的移动范围[L1,R1],更新L = man(L,L1) , R= ( R , R1)  。若L>R ,则说明不存在某个移动范围,能使得所有ai的区间都符合题意。

代码:

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#define x first
#define y second 

using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
const int N =4e5+10 , mod = 1e9 +7 ;

int n ,w,h;
int a[N],b[N] ;
int main()
{

	int T ; cin >>T;
	while( T --){
		cin >> n >>w >> h ;
		for(int i = 0 ; i < n ; ++i) cin >>a[ i ] ;
		for(int i = 0 ;i  < n ; ++i) cin >>b[i] ;
		int add = a[0]-w - ( b[0]-h) ;
		for(int i = 0 ; i < n ; ++i)  a[i] -= add ;
		bool flag= true;
		int l = -1e9 , r = 1e9 ;
		for(int i = 0 ; i < n ; ++i){
			int ll = b[i] -h , rr = b[i] +h ;
			int L = a[i] -w , R =a[i] +w ;
			l =  max(  l  ,  L-ll);
			r =  min( r,  R-rr) ;
			if( l > r ) break;
		}
		if( l  > r ) cout<<"NO" <<endl;
			else cout<<"YES" <<endl;

		
		
	}
	
	
    return 0 ;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值