Codeforces Round #709 (Div. 2, based on Technocup 2021 Final Round) 题解


#709 (Div. 2)

A. Prison Break

就是每个监狱破一扇门,输出 a × b a\times b a×b即可

B. Restore Modulo

就是取模意义下的操作,分大小操作,随便确定两个差值,然后构造即可

#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
#define maxn 100005
int T, n;
int a[maxn];

signed main() {
	scanf( "%lld", &T );
	while( T -- ) {
		scanf( "%lld", &n );
		for( int i = 1;i <= n;i ++ )
			scanf( "%lld", &a[i] );
		if( n == 1 ) {
			printf( "0\n" );
			continue;
		}
		int d1 = -1, d2 = -1;
		bool flag = 0;
		for( int i = 1;i < n;i ++ )
			if( a[i + 1] > a[i] )
				if( ~ d1 )
					if( a[i + 1] - a[i] != d1 ) {
						flag = 1;
						break;
					} else;
				else d1 = a[i + 1] - a[i];
			else
				if( ~ d2 )
					if( a[i] - a[i + 1] != d2 ) {
						flag = 1;
						break;
					} else;
				else d2 = a[i] - a[i + 1];
		if( flag ) printf( "-1\n" );
		else if( d1 == -1 || d2 == -1 ) printf( "0\n" );
			else {
				int maxx = 0;
				for( int i = 1;i <= n;i ++ ) maxx = max( maxx, a[i] );
				if( d1 + d2 <= maxx ) printf( "-1\n" );
				else printf( "%lld %lld\n", d1 + d2, ( a[2] + d1 + d2 - a[1] ) % ( d1 + d2 ) );
			}
	}
	return 0;
}

C. Basic Diplomacy

贪心,对于某天有多种选择的肯定优先满足限制比较死的,比如某天就必须和某人玩

发现,只要 k = 1 k=1 k=1的条件满足,后面只要使用次数没超过一半就可以跟那个人玩

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 100005
vector < int > G[maxn];
int T, n, m;
int cnt[maxn], ans[maxn], used[maxn];

int main() {
	scanf( "%d", &T );
	while( T -- ) {
		scanf( "%d %d", &n, &m );
		for( int i = 1;i <= n;i ++ ) cnt[i] = used[i] = 0;
		for( int i = 1;i <= m;i ++ ) G[i].clear();
		for( int i = 1, k;i <= m;i ++ ) {
			scanf( "%d", &k );
			for( int j = 1, x;j <= k;j ++ ) {
				scanf( "%d", &x );
				G[i].push_back( x );
				cnt[x] ++;
			}
			if( k == 1 ) ans[i] = G[i][0], used[G[i][0]] ++;
		}
		int lim = ( m + 1 ) >> 1;
		for( int i = 1;i <= m;i ++ )
			if( G[i].size() == 1 ) continue;
			else {
				for( int j = 0;j < G[i].size();j ++ )
					if( used[G[i][j]] >= lim ) continue;
					else {
						ans[i] = G[i][j];
						used[G[i][j]] ++;
					 	break;
					}
			}
		bool flag = 0;
		for( int i = 1;i <= n;i ++ )
			if( used[i] > lim ) {
				flag = 1;
				break;
			}
		if( flag ) printf( "NO\n" );
		else {
			printf( "YES\n" );
			for( int i = 1;i <= m;i ++ )
				printf( "%d ", ans[i] );
			printf( "\n" );
		}
	}
	return 0;
}

D. Playlist

想复杂了——就是分类大讨论

可以找两个质数,例如 1 e 9 + 9 1e9+9 1e9+9,遇到不互质的时候就赋质数

#include <cstdio>
#include <queue>
using namespace std;
#define maxn 100005
queue < int > q, ans;
int T, n;
int a[maxn], pos[maxn];
bool vis[maxn];

int gcd( int x, int y ) {
	if( ! y ) return x;
	else return gcd( y, x % y );
}

int main() {
	scanf( "%d", &T );
	while( T -- ) {
		scanf( "%d", &n );
		for( int i = 1;i <= n;i ++ ) vis[i] = pos[i] = 0;
		for( int i = 1;i <= n;i ++ ) scanf( "%d", &a[i] );
		for( int i = 1;i < n;i ++ )
			if( gcd( a[i], a[i + 1] ) == 1 )
				ans.push( i + 1 ), vis[i + 1] = 1, q.push( i ), pos[i] = i + 1, i ++;
		if( ! vis[n] && gcd( a[n], a[1] ) == 1 ) 
			ans.push( 1 ), vis[1] = 1, q.push( n ), pos[n] = 2;
		while( ! q.empty() ) {
			int t = q.front(); q.pop();
			if( vis[t] ) continue;
			if( pos[t] == n + 1 ) pos[t] = 1;
			while( vis[pos[t]] )
				pos[t] = pos[t] == n ? 1 : pos[t] + 1;
			if( gcd( a[pos[t]], a[t] ) == 1 ) 
				ans.push( pos[t] ), vis[pos[t]] = 1, q.push( t ), pos[t] ++;
		}
		printf( "%d ", ans.size() );
		while( ! ans.empty() ) printf( "%d ", ans.front() ), ans.pop();
		printf( "\n" );
	}
	return 0;
}

E. Skyline Photo

普通的 D P DP DP式子: d p i = d p j + w ( j + 1 , i ) dp_i=dp_j+w(j+1,i) dpi=dpj+w(j+1,i)

w ( j + 1 , i ) w(j+1,i) w(j+1,i)事实上是单调栈更新到 i i i时,栈里面第一个下标大于 j j j的房子的美丽度

对于栈里面的第 i i i个元素,管辖着区间 ( s i − 1 , s i ] (s_{i-1},s_i] (si1,si],维护的是递增的单调栈

f i = m a x k = 1 t o p ( m a x j = s k − 1 + 1 s k f j + b s k ) f_i=max_{k=1}^{top}(max_{j=s_{k-1}+1}^{s_k}f_j+b_{s_k}) fi=maxk=1top(maxj=sk1+1skfj+bsk)

#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
#define maxn 300005
int n, top;
int h[maxn], b[maxn], s[maxn], g[maxn], f[maxn], dp[maxn];

signed main() {
	scanf( "%lld", &n );
	for( int i = 1;i <= n;i ++ )
		scanf( "%lld", &h[i] );
	for( int i = 1;i <= n;i ++ )
		scanf( "%lld", &b[i] );
	g[0] = -1e18;
	for( int i = 1;i <= n;i ++ ) {
		int t = dp[i - 1];
		while( top && h[s[top]] > h[i] ) {
			t = max( t, f[top] );
			top --;
		}
		s[++ top] = i, f[top] = t;
		dp[i] = g[top] = max( g[top - 1], t + b[i] );
		/*
		(s[top-1],i]这一段被弹出的元素高度都比i高
		g[top-1]:把(s[top-1],i]这一段跟[1,s[top-1]]合并 答案就在比i矮的s[top-1]身上
		t+b[i]: (s[top-1],i]单独成段 最矮的就是i 
		*/
	}
	printf( "%lld\n", dp[n] );
	return 0;
}

F. Useful Edges

如果一条边 ( x , y , c ) (x,y,c) (x,y,c)和三元组 ( u , v , l ) (u,v,l) (u,v,l),满足 d i s u , x + c + d i s v , y ≤ l dis_{u,x}+c+dis_{v,y}\le l disu,x+c+disv,yl,则该边合法

目前来看是非常朴素的 O ( n 4 ) O(n^4) O(n4),数据范围只能承受 O ( n 3 ) O(n^3) O(n3),考虑降维(对角线想法)

d i s u , x + c + d i s v , y ≤ l ⇔ d i s v , y + c ≤ l − d i s u , x dis_{u,x}+c+dis_{v,y}\le l \Leftrightarrow dis_{v,y}+c\le l-dis_{u,x} disu,x+c+disv,yldisv,y+cldisu,x

只需要枚举 u , x , v u,x,v u,x,v便能求出 l − d i s u , x l-dis_{u,x} ldisu,x的最大值,再 u , x , y u,x,y u,x,y找到左边满足条件的边( v , y v,y v,y同地位循环)

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define int long long
#define maxn 605
pair < int, int > edge[maxn * maxn / 2];
int dis[maxn][maxn], w[maxn][maxn], l[maxn][maxn];
bool flag[maxn][maxn];
int u[maxn], v[maxn];
int n, m, Q, ans;

signed main() {
	memset( dis, 0x3f, sizeof( dis ) );
	scanf( "%lld %lld", &n, &m );
	for( int i = 1, u, v, c;i <= m;i ++ ) {
		scanf( "%lld %lld %lld", &u, &v, &c );
		edge[i] = make_pair( u, v );
		w[u][v] = w[v][u] = dis[u][v]= dis[v][u] = c;
	}
	for( int i = 1;i <= n;i ++ )
		dis[i][i] = 0;
	for( int k = 1;k <= n;k ++ )
		for( int i = 1;i <= n;i ++ )
			for( int j = 1;j <= n;j ++ )
				dis[i][j] = min( dis[i][j], dis[i][k] + dis[k][j] );
	scanf( "%lld", &Q );
	while( Q -- ) {
		int u, v, x;
		scanf( "%lld %lld %lld", &u, &v, &x );
		l[u][v] = l[v][u] = max( l[u][v], x );
	}
	for( int v = 1;v <= n;v ++ ) {
		for( int x = 1;x <= n;x ++ ) {
			int maxx = 0;
			for( int u = 1;u <= n;u ++ )
				maxx = max( maxx, l[u][v] - dis[u][x] );
			for( int y = 1;y <= n;y ++ ) {
				if( dis[y][v] + w[x][y] <= maxx ) 
					flag[x][y] = flag[y][x] = 1;
			}
		}
	}
	int ans = 0;
	for( int i = 1;i <= m;i ++ )
		if( flag[edge[i].first][edge[i].second] ) ans ++;
	printf( "%lld\n", ans );
	return 0;
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值