[AtCoder Beginner Contest 216] 题解


比赛链接

A - Signed Difficulty

签到题

#include <cstdio>
int x, y;
char c;
int main() {
	scanf( "%d%c%d", &x, &c, &y );
	if( y <= 2 ) printf( "%d-", x );
	else if( y >= 7 ) printf( "%d+", x );
	else printf("%d", x );
	return 0;
}

B - Same Name

签到题

#include <cstdio>
#include <iostream>
#include <map>
using namespace std;
map < pair < string, string >, int > mp;
int n;

int main() {
	scanf( "%d", &n );
	for( int i = 1;i <= n;i ++ ) {
		string a, b;
		cin >> a >> b;
		if( mp[make_pair( a, b )] ) return ! printf( "Yes\n" );
		mp[make_pair( a, b )] = 1;
	}
	printf( "No\n" );
	return 0;
}

C - Many Balls

简单题

发现是快速幂的逆过程

#include <cstdio>
#include <vector>
using namespace std;
#define int long long
vector < int > ans;
int n;

signed main() {
	scanf( "%lld", &n );
	while( n ) {
		if( n & 1 ) ans.push_back( 0 );
		ans.push_back( 1 );
		n >>= 1;
	}
	for( int i = ans.size() - 1;~ i;i -- )
		printf( "%c", ans[i] + 'A' );
	return 0;
}

D - Pair of Balls

超难题!!!靠

不搞环,硬汉就搞大模拟

记录颜色两次出现位置,并带上标记,成为堆顶为 0 0 0,在下面为 1 1 1

当颜色两个位置都入队(成为其所在堆的堆顶)

弹出,并把堆下面一个元素标记为新的堆顶

就这么模拟,凸(艹皿艹 )

之前打的什么鬼傻逼set/map半天模拟不动,又TLEREWA

o(╥﹏╥)o _(¦3」∠)_ ┏┛墓┗┓…(((m-__-)m

#include <cstdio>
#include <vector>
#include <queue>
#include <iostream>
using namespace std;
#define maxn 200005
struct node {
	int color, i, rnk, pos;
	node(){}
	node( int C, int I, int R, int P ) {
		color = C, i = I, rnk = R, pos = P;
	}
}c[maxn][2];
int n, m, cnt;
queue < node > q;
vector < int > g[maxn];
bool vis[maxn][2];

int main() {
	scanf( "%d %d", &n, &m );
	for( int i = 1, k, x;i <= m;i ++ ) {
		scanf( "%d", &k );
		for( int j = 1;j <= k;j ++ ) {
			scanf( "%d", &x );
			g[i].push_back( x );
			if( ! vis[x][0] ) c[x][0] = node( x, i, j, 1 ), vis[x][0] = 1;
			else c[x][1] = node( x, i, j, 1 ), vis[x][1] = 1;
		}
	}
	for( int i = 1;i <= n;i ++ ) {
		auto ls = c[i][0];
		auto rs = c[i][1];
		vis[i][0] = vis[i][1] = 0;
		if( ! ls.pos and ! rs.pos ) {
			q.push( c[i][0] );
			q.push( c[i][1] );
		}
	}
	while( ! q.empty() ) {
		auto ls = q.front(); q.pop();
		auto rs = q.front(); q.pop();
		cnt ++;
		int nxt;
		if( ls.rnk ^ g[ls.i].size() ) {
			nxt = g[ls.i][ls.rnk];
			if( c[nxt][0].i == ls.i ) c[nxt][0].pos = 0;
			else c[nxt][1].pos = 0;
			if( ! c[nxt][0].pos and ! c[nxt][1].pos ) {
				q.push( c[nxt][0] );
				q.push( c[nxt][1] );
			}
		}
		if( rs.rnk ^ g[rs.i].size() ) {
			nxt = g[rs.i][rs.rnk];
			if( c[nxt][0].i == rs.i ) c[nxt][0].pos = 0;
			else c[nxt][1].pos = 0;
			if( ! c[nxt][0].pos and ! c[nxt][1].pos ) {
				q.push( c[nxt][0] );
				q.push( c[nxt][1] );
			}
		}
	}
	if( cnt ^ n ) printf( "No\n" );
	else printf( "Yes\n" );
	return 0;
}

E - Amusement Park

简单题

数学直接算

从大到小取,每次 a [ i ] a[i] a[i]取到 a [ i + 1 ] a[i+1] a[i+1]相同值就停,等差数列算这中间的值和

随着 i i i的枚举,前面全是相同的 a [ i ] a[i] a[i],所以这等差数列算了后还要乘以一个 i i i的系数

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

signed main() {
	scanf( "%lld %lld", &n, &k );
	for( int i = 1;i <= n;i ++ )
		scanf( "%lld", &a[i] );
	sort( a + 1, a + n + 1, []( int x, int y ) { return x > y; } );
	int ans = 0;
	for( int i = 1;i <= n;i ++ ) {
		int x = a[i] - a[i + 1];
		int sum = ( a[i] + a[i + 1] + 1 ) * x / 2;
		if( k <= x * i ) {
			x = k / i;
			sum = ( a[i] + a[i] - x + 1 ) * x / 2;
			ans += sum * i;
			a[i] -= k / i;
			k %= i;
			ans += a[i] * k;
			break;
		}
		else {
			ans += sum * i;
			k -= x * i;
		}
	}
	printf( "%lld\n", ans );
	return 0;
}

F - Max Sum Counting

简单题

凸(艹皿艹 ),傻逼背包倒DP调nm半个小时

A A A排序,枚举 A i A_i Ai作为最大值

接下来就是选一些数满足 B j B_j Bj的和不超过 A i A_i Ai

d p j : dp_j: dpj: 和为 j j j的方案数

每次统计答案后,再转移 i i i B i B_i Bi的贡献

发现其实就是个简单背包

#include <cstdio>
#include <algorithm>
using namespace std;
#define int long long
#define mod 998244353
#define maxn 5005
struct node { int a, b; }g[maxn];
int n;
int t[maxn], dp[maxn];

bool cmp( node x, node y ) {
	return x.a < y.a;
}

signed main() {
	scanf( "%lld", &n );
	for( int i = 1;i <= n;i ++ ) scanf( "%lld", &g[i].a );
	for( int i = 1;i <= n;i ++ ) scanf( "%lld", &g[i].b );
	sort( g + 1, g + n + 1, cmp );
	int ans = 0;
	dp[0] = 1;
	for( int i = 1;i <= n;i ++ ) {
		for( int j = 0;j <= g[i].a - g[i].b;j ++ )
			ans = ( ans + dp[j] ) % mod;
		for( int j = maxn - 1;j >= g[i].b;j -- )
			dp[j] = ( dp[j] + dp[j - g[i].b] ) % mod;
	}
	printf( "%lld\n", ans );
	return 0;
}

G - 01Sequence

看完题目是非常板的差分约束题面感

s i s_i si表示 1 − i 1-i 1i1的个数

[ l , r ] [l,r] [l,r]区间内1个数不少于 x x x个,翻译约束为 s r − s l − 1 ≥ x s_r-s_{l-1}\ge x srsl1x

为了契合差分约束的 ≤ \le

重新定义 s i s_i si表示 1 − i 1-i 1i0的个数

翻译约束为 s r − s l − 1 ≤ r − l + 1 − x s_r-s_{l-1}\le r-l+1-x srsl1rl+1x

转化到图论上,连边 l − 1 → r l-1\rightarrow r l1r有向边的边权 r − l + 1 − x r-l+1-x rl+1x

跑最短路即可

本题卡SPFA

差分约束

  • 为什么是 ≤ \le 约束(不等式左边为“点”,不等式右边为固定限制)

    • 首先最长路目前没有快速地算法
    • 其次符合最短路 d i s v ≤ d i s u + w u , v dis_v\le dis_u+w_{u,v} disvdisu+wu,v的形式
  • 感觉最后求 s n s_n sn的最大值,应该是最长路,为什么图论上是跑最短路

    • 首先约束和 s n s_n sn之间是逆向关系,并不是正比于

    • 可以理解为最短路就是求出最严格的约束

    • s n s_n sn的所有值都保证要满足所有的约束,自然要满足最严格的约束

    • e.g. ( i , j ) = x 1 , ( j , k ) = x 2 , ( i , k ) = x 3 , x 1 + x 2 < x 3 (i,j)=x_1,(j,k)=x_2,(i,k)=x_3,x_1+x_2<x_3 (i,j)=x1,(j,k)=x2,(i,k)=x3,x1+x2<x3

      既要满足 d i s j ≤ d i s i + x 1 ; d i s k ≤ d i s j + x 2 dis_j\le dis_i+x_1;dis_k\le dis_j+x_2 disjdisi+x1;diskdisj+x2

      又要满足 d i s k ≤ d i s i + x 3 dis_k\le dis_i+x_3 diskdisi+x3

      并且 d i s k ≤ d i s j + x 2 ≤ d i s i + x 1 + x 2 ≤ d i s i + x 3 dis_k\le dis_j+x_2\le dis_i+x_1+x_2\le dis_i+x_3 diskdisj+x2disi+x1+x2disi+x3

      根据不等式的原则,同小取小的最严格约束

      这恰好匹配最短路的过程

所以差分约束的本质是转化为最短路问题

#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
#define maxn 200005
#define Pair pair < int, int >
priority_queue < Pair, vector < Pair >, greater < Pair > > q;
int n, m;
int dis[maxn];
bool vis[maxn];
vector < Pair > G[maxn];

void addedge( int u, int v, int w ) {
	G[u].push_back( make_pair( v, w ) );
}

int main() {
	scanf( "%d %d", &n, &m );
	for( int i = 1, l, r, x;i <= m;i ++ ) {
		scanf( "%d %d %d", &l, &r, &x );
		addedge( l - 1, r, r - l + 1 - x );
	}
	for( int i = 0;i < n;i ++ ) {
		addedge( i, i + 1, 1 );
		addedge( i + 1, i, 0 );
	}
	memset( dis, 0x3f, sizeof( dis ) );
	dis[0] = 0; q.push( make_pair( 0, 0 ) );
	while( ! q.empty() ) {
		Pair now = q.top(); q.pop();
		int u = now.second;
		if( vis[u] or dis[u] ^ now.first ) continue;
		vis[u] = 1;
		for( auto nxt : G[u] ) {
			int v = nxt.first, w = nxt.second;
			if( dis[v] > dis[u] + w ) {
				dis[v] = dis[u] + w;
				q.push( make_pair( dis[v], v ) );
			}
		}
	}
	for( int i = 1;i <= n;i ++ )
		printf( "%d ", ( dis[i] - dis[i - 1] ) ^ 1 );
	return 0;
}
  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值