AtCoder Beginner Contest 214

AtCoder Beginner Contest 214

C - Distribution

题目

\(N\)个人,第\(i\)个人会在\(T_i\)时刻从Takahashi手上拿到宝石.若第\(i\)个人在\(t_i\)时刻(不一定等于\(T_i\))拿到宝石,他会在\(t_i+S_i\)时刻将宝石给\((i-1)%n+1\)个人.

给定\(N,S,T\)问每个人拿到宝石的最早时间.

思路

先把\(T_i\)和对应的\(i\)捆绑起来,扔进按\(T\)从小到大排序的小根堆,每次从小根堆中取出\(t_i\)\(i\),则\(ans_i=t_i\),将\(t_i+S_i\)\((i-1)\%n+1\)扔进堆里即可.

代码

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
int read() {
	int re = 0;
	char c = getchar();
	bool negt = false;
	while(c < '0' || c > '9')
		negt |= (c == '-') , c = getchar();
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0' , c = getchar();
	return negt ? -re : re;
}

const int N = 200010;
int n;
int s[N] , t[N];
int ans[N];
struct node {
	int t , id;
	bool operator < (const node &a) const {
		return t > a.t;
	}
};
node make(int t , int id) {
	node tmp;
	tmp.t = t , tmp.id = id;
	return tmp;
}
priority_queue <node> q;
int main() {
	n = read();
	for(int i = 1 ; i <= n ; i++)
		s[i] = read();
	for(int j = 1 ; j <= n ; j++) {
		t[j] = read();
		q.push(make(t[j] , j));
	}
	while(!q.empty()) {
		node k = q.top();
		q.pop();
		if(ans[k.id] != 0)
			continue;
		ans[k.id] = k.t;
		q.push(make(k.t + s[k.id] , k.id % n + 1));
	}
	
	for(int i = 1 ; i <= n ; i++)
		printf("%d\n" , ans[i]);
	return 0;
}

D - Sum of Maximum Weights

题目

给定一颗树,有\(N\)个结点,定义\(f(u,v)\)\(u\),\(v\)​路径上边权的最大值,求\(\sum_{i=1}^{N-1}\sum_{j=i+1}^Nf(i,j)\)​.

思路

把边按照边权从小到大排序,并按顺序遍历所有边,对于每条边,它对答案的贡献为

\[w_i\cdot size_{u_i}\cdot size_{v_i} \]

\(size\)为点所属并查集的大小,计算答案后,合并\(i,j\)所在并查集.

代码

#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>

int read() {
	int re = 0;
	char c = getchar();
	bool negt = false;
	while(c < '0' || c > '9')
		negt |= (c == '-') , c = getchar();
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0' , c = getchar();
	return negt ? -re : re;
}

typedef long long lint;
const int N = 200010;
int n;

struct EDGE {
	int u , v;
	lint w;
}ed[N];
bool cmp(EDGE x , EDGE y) {
	return x.w < y.w;
}

lint size[N];
int fa[N];
int findroot(int x) {
	return fa[x] == x ? x : (fa[x] = findroot(fa[x]));
}
void uni(int x , int y) {
	x = findroot(x) , y = findroot(y);
	if(x == y)
		return ;
	size[x] += size[y];
	fa[y] = x;
}

int main() {
	n = read();
	for(int i = 1 ; i < n ; i++)
		ed[i].u = read() , ed[i].v = read() , ed[i].w = read();
	
	for(int i = 1 ; i <= n ; i++)
		fa[i] = i , size[i] = 1;
	std::sort(ed + 1 , ed + n , cmp);
	lint ans = 0;
	for(int i = 1 ; i < n ; i++) {
		int x = findroot(ed[i].u) , y = findroot(ed[i].v);
		ans += ed[i].w * size[x] * size[y];
		uni(x , y);
	}
	std::cout << ans;
	return 0;
}

E - Packing Under Range Regulations

题目

\(10^9\)个盒子编号\(1\)\(10^9\),每个盒子可以放一个球,第\(i\)个球可以放在编号\(l_i\)\(r_i\)的盒子中.给定\(l,r\),问:是否每个球都能放在盒子中.

思路

把每个球按照\(l\)从小到大排序,假设当前需要用编号为\(p\)的盒子,且编号为\(1\)\(p-1\)的盒子已经不可用,有\(j\)还没有放入任何一个盒子的小球,且它们都可以放在\(p\)盒子中.

我们肯定要先把\(r\)小的先放到盒子里,因为前面的盒子已经不可用,\(r\)​大的还有可能放进后面的盒子.

因此,我们的贪心就出来了,详见代码.

居然被一些奇奇怪怪的小错误卡了半天.

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
int read() {
	int re = 0;
	char c = getchar();
	bool negt = false;
	while(c < '0' || c > '9')
		negt |= (c == '-') , c = getchar();
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0' , c = getchar();
	return negt ? -re : re;
}

typedef long long lint;
const int N = 2000010;
int n;

struct BALL {
	int l , r;
	bool operator < (const BALL &b) const{
		return r > b.r;
	}
}ball[N];
bool cmp(BALL a , BALL b) {
	return a.l < b.l;
}

priority_queue <BALL> q;
int main() {
	int T = read();
	while(T--) {
		while(q.size())q.pop();
		n = 0;
		
		n = read();
		for(int i = 1 ; i <= n ; i++)
			ball[i].l = read() , ball[i].r = read();
		
		sort(ball + 1 , ball + n + 1 , cmp);//左端点排序
		bool ans = true;
		int box = ball[1].l , p = 1;
		while(p <= n || !q.empty()) {
			while(ball[p].l <= box && p <= n)//可以放进编号为box的小球放入堆内,堆维护最小r
				q.push(ball[p]), ++p;
			
			if(q.empty()) {//跳过无用盒子
				box = ball[p].l;
				continue;
			}
			BALL b = q.top();//尝试将小球b放入编号为box的盒子
			q.pop();
			if(b.r < box) {//放不了
				ans = false;
				break;
			}
			++box;
		}
		puts(ans ? "Yes" : "No");
	}
	return 0;
}

F - Substrings

题目

有一个字符串\(S\),你可以确定一个序列\(a_1,a_2,\cdots a_k\),满足\(\forall i\in[1,k)\quad ,a_i<a_{i+1}-1\),且\(k>0\).定义字符串\(T=S_{a_1}S_{a_2}S_{a_3}\cdots S_{a_k}\).

问有多少个不同的\(T\)

思路

考虑\(a_i<a_{i+1}\)的情况怎么做.

\(f_i\)​表示在从前\(i\)​个中选择若干字符,其中必选\(i\)​,按原本顺序能构不同的字符串的个数.则有:

\[f_i=\sum_{j=k}^{i-1}f_j \]

其中,\(\forall x\in(k,i),S_x\neq S_i\)​,且\(S_k=S_i,k<i\)​,若不存在\(S_k=S_i\)\(k=0\).

特殊地,\(f_0=1\),表示空串.

由于\(T\)不能为空,答案就是\(\sum_{i=1}^nf_i\).

回到这题.

\(a_i<a_{i+1}-1\).

其实变化多少,只要让\(i-j>2\)时才累加\(f_j\)即可,但是这样你会发现\(i=1\)时并不会累加\(f_0\),这不是我们想要的结果,所以,为了方便,我们让\(f_{-1}=0\).

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
using namespace std;
const int N = 200010;
const int mod = (int)1e9+7;

int __f[N] , *f = __f + 3;
char s[N];
int n;
signed main() {
	scanf("%s" , s + 1);
	n = strlen(s + 1);
	
	f[-1] = 1;
	for(int i = 1 ; i <= n ; i++) {
		for(int j = i - 1 ; true ; --j) {
			if(i - j >= 2)
				f[i] += f[j];
			if(s[i] == s[j])
				f[i] += f[j - 1];
			if(j == -1 || s[j] == s[i])
				break;
		}
		f[i] %= mod;
	}
	int ans = 0;
	for(int i = 1 ; i <= n ; i++)
		ans += f[i];
	cout << ans % mod;
	return 0;
} 
posted @ 2021-08-15 20:29  追梦人1024  阅读(130)  评论(0编辑  收藏  举报