AtCoder Beginner Contest 346

A - Adjacent Product

题意:

在这里插入图片描述

代码:

int n, a[maxn];
void solve(){
	cin >> n;
	for(int i = 1; i <= n; i++)
		cin >> a[i];
	for(int i = 1; i < n; i++)
		cout << a[i] * a[i+1] << " \n"[i == n-1];
	return;
}

B - Piano

题意:

在这里插入图片描述

代码:

int prew[maxn], preb[maxn];
int w, b;
void solve(){
	string s = "wbwbwwbwbwbw";
	for(int i = 1; i <= 20; i++)
		s += s;
	s = "?" + s;
	for(int i = 1; i <= 200; i++){
		prew[i] = prew[i-1] + (s[i] == 'w');
		preb[i] = preb[i-1] + (s[i] == 'b');
	}
	cin >> w >> b;
	auto check = [&](int l, int r){
		int cntw = prew[r] - prew[l-1];
		int cntb = preb[r] - preb[l-1];
		return w == cntw && cntb == b;
	};
	int flag = 0;
	for(int i = 1; i <= 12; i++){
		int len = w+b;
		int l = i;
		int r = l+len-1;
		if(check(l, r) == true){
			flag = 1;
			break;
		}
	}
	if(flag)
		cout << "Yes" << endl;
	else
		cout << "No" << endl;
	
	return;
}

C - Σ

题意:

在这里插入图片描述

代码:

ll n, k, x;
ll a[maxn];
void solve(){
	cin >> n >> k;
	set<ll> s;
	ll sum = (k+1) * k / 2;
	for(int i = 1; i <= n; i++){
		cin >> x;
		if(x <= k){
			if(s.find(x) == s.end())
				sum -= x;
			s.insert(x);
		}
	}
	cout << sum << endl;
	return;
}

D - Gomamayo Sequence

题意:

在这里插入图片描述

解析:

恰好一个位置 i i i 可以与 i + 1 i+1 i+1 处相同。

f i , 0 / 1 f_{i,0/1} fi,0/1 为前 i i i 个位置相邻位置不同的最小代价, f i , 0 f_{i,0} fi,0 为不修改位置 i , f i , 1 f_{i,1} fi,1 为修改位置 i i i
g i , 0 / 1 g_{i,0/1} gi,0/1 为从 i i i 到最后相邻位置不同的最小代价。

枚举相同的位置 i i i,计算对答案的贡献。

代码:

int n;
string s;
ll f[maxn][2], g[maxn][2];
ll c[maxn];
void solve(){
	cin >> n >> s;
	for(int i = 1; i <= n; i++)
		cin >> c[i];
	s = "?" + s;
	ll ans = INF;
	
	f[1][0] = 0, f[1][1] = c[1];	
	for(int i = 2; i <= n; i++){
		if(s[i] == s[i-1]){
			f[i][0] = f[i-1][1];
			f[i][1] = f[i-1][0] + c[i];
		}			
		else{
			f[i][0] = f[i-1][0];
			f[i][1] = f[i-1][1] + c[i];
		}		
	}	
	g[n][0] = 0, g[n][1] = c[n];
	for(int i = n-1; i >= 1; i--){
		if(s[i] == s[i+1]){
			g[i][0] = g[i+1][1];
			g[i][1] = g[i+1][0] + c[i];
		}			
		else{
			g[i][0] = g[i+1][0];
			g[i][1] = g[i+1][1] + c[i];
		}
	}
	
	for(int i = 1; i < n; i++){		
		if(s[i] == s[i+1]){
			ll res = min(f[i][1] + g[i+1][1], f[i][0] + g[i+1][0]);
			ans = min(ans, res);
		}
		else{
			ll res = min(f[i][1] + g[i+1][0], f[i][0] + g[i+1][1]);
			ans = min(ans, res);
		}
	}
	cout << ans << endl;
	return;
}

E - Paint

题意:

在这里插入图片描述

解析:

倒序遍历操作序列,维护当前所剩的行和列。

对于涂一行操作 ( 1 , a , x ) (1, a, x) (1,a,x),如果第 a 行还存在,此时列数为 b,则会有 b 个位置被涂为颜色 x,然后将第 a 行删除。涂列操作同理

代码:

#define int ll
int h, w, m;
struct oper{
	int op, p, x;
	oper(int op, int p, int x) : op(op), p(p), x(x){}
};

void solve(){
	cin >> h >> w >> m;
	vector<oper> ops;
	map<int, int> res;
	unordered_set<int> col, row;
	for(int i = 1; i <= h; i++)
		row.insert(i);
	for(int i = 1; i <= w; i++)
		col.insert(i);
	for(int i = 1, op, p, x; i <= m; i++){
		cin >> op >> p >> x;
		ops.push_back(oper(op, p, x));
	}
	while(ops.size()){
		oper op = ops.back();
		ops.pop_back();
		if(op.op == 1){
			auto it = row.find(op.p);
			if(it != row.end()){
				res[op.x] += col.size();
				row.erase(it);
			}
		}
		else if(op.op == 2){
			auto it = col.find(op.p);
			if(it != col.end()){
				res[op.x] += row.size();
				col.erase(it);
			}
		}
		if(col.size() == 0 || row.size() == 0) break;
	}
	if(row.size() && col.size())
		res[0] += row.size() * col.size();
	cout << res.size() << endl;
	for(auto it = res.begin(); it != res.end(); it++){
		if(it->second == 0) continue;
		cout << it->first << " " << it->second << endl;
	}
	return;
}

F - SSttrriinngg in StringString

题意:

在这里插入图片描述

解析:

二分答案。

对于检查二分值 x 是否可行:因为是子序列,所以贪心的进行匹配,判断需要的串 s s s 的个数是否大于 n n n

因为需要记录s串已经匹配的位置,以及s串里每个字符的位置。所以预处理出s串字符个数的前缀和,以及每个字符的位置。

二分的右边界为1e17。

代码:

int sum[maxn][30];
void solve(){
	string s, t;
	int n;
	cin >> n >> s >> t;
	int lens = s.length();
	int lent = t.length();
	s = "?" + s;
	t = "?" + t;
	vector<vector<int>> pos(26);
	for(int i = 1; i <= lens; i++){
		for(int j = 0; j < 26; j++)
			sum[i][j] = sum[i-1][j];
		sum[i][s[i]-'a']++;
		pos[s[i]-'a'].push_back(i);
	}
	auto check = [&](int x){
		int cnt = 0;
		int p = 0;
		for(int i = 1; i <= lent && cnt <= n; i++){
			int ch = t[i] - 'a';
			int num = sum[lens][ch];
			if(num == 0) return false;
			
			int need = x + sum[p][ch];
			cnt += (need + num - 1) / num - 1;
			need = need % num;
			if(need == 0) p = pos[ch].back();
			else p = pos[ch][need-1];
		}		
		return cnt <= n;
	};
	int l = 1, r = 1e17;
	int ans = 0;
	while(l <= r){
		int mid = (l+r) >> 1;
		if(check(mid)){
			l = mid+1;
			ans = mid;
		}
		else
			r = mid-1;
	}
	cout << ans << endl;
	return;
}

G - Alone

题意:

在这里插入图片描述

解析:

假设 a [ i ] = x a[i] = x a[i]=x

位置 i i i 左侧距离 i i i 最近的 a [ j ] = x a[j] = x a[j]=x,令 p r e [ i ] = j pre[i] = j pre[i]=j;位置 i i i 右侧距离 i i i 最近的 a [ j ] = x a[j] = x a[j]=x,令 s u f [ i ] = j suf[i] = j suf[i]=j

位置 i i i 对答案的贡献为 ( i − p r e [ i ] ) × ( s u f [ i ] − i ) (i-pre[i]) \times (suf[i]-i) (ipre[i])×(suf[i]i),可以看成长为 ( i − p r e [ i ] ) (i-pre[i]) (ipre[i]) ,宽为 ( s u f [ i ] − i ) (suf[i]-i) (suf[i]i) 的矩形的面积。

在一个区间内,可能有多个数字只出现一次,所以求的是矩形面积并,而不是矩形面积和。

扫描线求矩形面积并。

代码:

struct line{
	int l, r, h;
	int v;
	line(){}
	line(int l, int r, int h, int v) : l(l), r(r), h(h), v(v){}
	bool operator < (const line &b) const{
		return h < b.h;
	} 
};

struct node{
	int l, r, cnt;
	ll len;
}t[maxn << 4];
vector<int> X;

int pre[maxn], suf[maxn], pos[maxn];
int n, a[maxn];
ll ans;

int ls(int k){return k << 1;}
int rs(int k){return k << 1 | 1;}
void pushup(int k){
	int l = t[k].l, r = t[k].r;
	if(t[k].cnt)
		t[k].len = X[r+1] - X[l];
	else
		t[k].len = t[ls(k)].len + t[rs(k)].len;
}
void build(int k, int l, int r){
	t[k].l = l, t[k].r = r;
	t[k].cnt = 0;
	t[k].len = 0; 
	if(l == r)
		return;
	int mid = (l+r) >> 1;
	build(ls(k), l, mid);
	build(rs(k), mid+1, r);
} 
void modify(int k, int x, int y, int v){
	if(x <= t[k].l && y >= t[k].r){
		t[k].cnt += v;
		pushup(k);
		return;
	}
	int mid = (t[k].l + t[k].r) >> 1;
	if(x <= mid)
		modify(ls(k), x, y, v);
	if(y > mid)
		modify(rs(k), x, y, v);
	pushup(k);
} 


void solve(){
	cin >> n;
	for(int i = 1; i <= n; i++)
		cin >> a[i];			
	for(int i = 1; i <= n; i++){
		pre[i] = pos[a[i]];
		pos[a[i]] = i;
	}
	for(int i = 1; i <= n; i++)
		pos[i] = n+1;
	for(int i = n; i >= 1; i--){
		suf[i] = pos[a[i]];
		pos[a[i]] = i;
	}
	
	vector<line> lines;	
	X.push_back(-1);
	for(int i = 1; i <= n; i++){
		int x1 = pre[i]+1, x2 = i+1, y1 = i, y2 = suf[i];
		lines.push_back(line(x1, x2, y1, 1));
		lines.push_back(line(x1, x2, y2, -1));
		X.push_back(x1);
		X.push_back(x2);	
	}
		
	sort(X.begin(), X.end());	
	X.erase(unique(X.begin(), X.end()), X.end());
	int m = X.size();
	build(1, 1, m-2);
	sort(lines.begin(), lines.end());
	for(int i = 0; i < lines.size()-1; i++){
		int l = lower_bound(X.begin(), X.end(), lines[i].l) - X.begin();
		int r = lower_bound(X.begin(), X.end(), lines[i].r) - X.begin();
		modify(1, l, r-1, lines[i].v);
		ans += t[1].len * (lines[i+1].h - lines[i].h);
	}
	cout << ans << endl;
	
	return;
}
  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值