Codeforces Round #546 (Div. 2) 题解

34 篇文章 0 订阅
24 篇文章 0 订阅

题目链接:http://codeforces.com/contest/1136

 

A. Nastya Is Reading a Book

水题。

#include <bits/stdc++.h>
#define fi first
#define se second 
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
typedef long long ll;
typedef pair <int,ll> pa;
const int mx = 1e3 + 10;
int a[mx],b[mx];
int main()
{
	int n,k;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d%d",a+i,b+i);
	scanf("%d",&k);
	for(int i=1;i<=n;i++) if(k<=b[i]) 
	return 0*printf("%d\n",n-i+1);
	return 0;
}

B. Nastya Is Playing Computer Games

很显然n个沙井只有一个会出现扔两块石头的情况。因为但第一个沙井被打开时,其他石头都往那边扔就好了。

扔石头和开井盖的次数就是3+2*(n-1),移动次数就是(n-1)+min(k-1,n-k),整合为3*n + min(k-1,n-k)

#include <bits/stdc++.h>
#define fi first
#define se second 
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
typedef long long ll;
typedef pair <int,ll> pa;
const int mx = 1e5 +10;
int main()
{
	int n,k;
	scanf("%d%d",&n,&k);
	printf("%d\n",3*n+min(k-1,n-k));//2*(n-1)+3+(n-1)+min(k-1,n-k) 
	return 0;
}

C. Nastya Is Transposing Matrices

将二维数组斜着看,枚举n+m-1个斜边,保证a中每个斜边中的数是b的一个排列,那么就有解,否则无解。

(试着从上到下从左到右看每个点能更新的情况就能推出)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#include<map>
#include<cstring>
using namespace std;
typedef long long ll;
const int mx = 1e3 + 10;
int a[mx][mx],b[mx][mx]; 
map <int,int> mp;
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%d",a[i]+j);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%d",b[i]+j);
		}
	}
	int x , y;
	for(int i=1;i<=m;i++){
		x = 1, y = i;
		mp.clear();
		while(y&&x<=n) mp[a[x][y]]++,x++,y--;
		x = 1,y = i;
		while(y&&x<=n){
			if(!mp[b[x][y]]) return 0*puts("NO");
			mp[b[x][y]]--;
			x++ , y--;
		}
	}
	for(int i=2;i<=n;i++){
		x = i, y = m;
		mp.clear();
		while(y&&x<=n) mp[a[x][y]]++,x++,y--;
		x = i, y = m;
		while(y&&x<=n){
			if(!mp[b[x][y]]) return 0*puts("NO");
			mp[b[x][y]]--;
			x++ , y--; 
		}
	}
	puts("YES");
    return 0;
}

D. Nastya Is Buying Lunch

很明显,愿意和主角并且可以换成功的人都会在主角排名的后面。

假设主角现在在i位置,j位置的人想和他换,那么就必须和(i,j)中的人一个个换过去,并且这些都是没有和主角成功交换的。

所以做法就很显然了。


 #include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<cstring>
using namespace std;
typedef long long ll;
const int mx = 3e5 + 10;
vector <int> g[mx];
int a[mx],pos[mx];
bool vis[mx];
int main(){
	int n,m,u,v;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",a+i),pos[a[i]] = i;
	for(int i=0;i<m;i++){
		scanf("%d%d",&u,&v);
		g[u].push_back(v);
	}
	int ans = 0;
	for(int i=n-1;i;i--){
		u = a[i];
		int c = 0;
		for(int v:g[u]){
			if(pos[v]>pos[u]&&!vis[v])
			c++;
		}	
		if(n-i==c+ans) ans++,vis[u] = 1;
	}
	printf("%d\n",ans);
    return 0;
}

E. Nastya Hasn't Written a Legend

本题做法"暴力寻找更新区间"+懒人线段树区间维护

设在l处给a[l]加上x,如果a[l]+x+k[i] > a[i+1],那么a[i+1]就会被赋值a[i]+x+k[i]。设v[i]表示a[i+1]-a[i]-k[i]。

那就原来的a[i+1]而言,a[i+1]增加了x-v[i],如果a[i+1]+k[i+1] > a[i+2],那么就原来的a[i+2]而言,a[i+2]增加了x-v[i]-v[i+1]。

所以如果在l出加上x,会被更新的区间就是将l看做起始点,v[i]前缀和最后一个<=x的那个。

那么就二分?NO,NO,NO。如果此次的更新使得a[i+1]==a[i]+k[i],我们可以将它看做在i处处于"平衡",这里的v[i]就是0了,可以不用去计算,直接跳过,又在l处更新只会破坏l-1的平衡点,使得v[l-1]>0,所以最多也就每次新插入一个点值,所以这个完全可以用一个set来"暴力"找出l出更新的"r"在哪里。set中只保存v[i]>0的点,最多插入q+n次,所以这个操作是q*log(n)的。

接下来我们就知道了需要更新的区间了,那么怎么更新呢?假设需要更新[l,r],那么有:

a[l] = a[l],a[l+1] = a[l] + k[l],a[l+2] = a[l] + k[i] + k[i+1],a[l+3] = a[l] + k[i] + k[i+1] + k[i+2],a[l+4]=....

直接看代码吧,类似前缀和加减(应该都看得懂)

for(int i=n-1;i>=0;i--){
	suf[i] = suf[i+1] + (n-i)*b[i];//以i点为起始点的前缀和的前缀和 
	c[i] = c[i+1] + b[i];//以i点为起始点的前缀和 
}
ll Get(int l,int r,int hd,ll a[hd]){
	ll v = c[hd] - c[l-1];
	ll u = suf[l-1] - suf[r] - (n-r)*(c[l-1]-c[r]);
	return (a[hd]+v)*(r-l+1) + u;
}

这样就能快速算出区间[l,r]的和。

接下来懒人标记什么的基本操作用上就ok了。

#include <bits/stdc++.h>
#define fi first
#define se second 
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
typedef long long ll;
typedef pair <int,ll> pa;
const int mx = 1e5 +10;
ll a[mx],b[mx],add[mx]; 
int n,q,L,R;
ll suf[mx],c[mx];
set <pa> st;
struct node
{
	int head;
	ll sum,d;	
}s[mx<<2];
void build(int l,int r,int rt)
{
	if(l==r){
		s[rt].sum = a[l];
		s[rt].head = l;
		return ;
	}
	int mid = (l+r)>>1;
	build(lson);build(rson);
	s[rt].sum = s[rt<<1].sum + s[rt<<1|1].sum; 
}
ll Get(int l,int r,int hd,ll d){
	ll v = c[hd] - c[l-1],u = suf[l-1] - suf[r] - (n-r)*(c[l-1]-c[r]);
	return (d+v)*(r-l+1) + u;
}
void push_down(int l,int r,int rt)
{
	if(s[rt].head){
		int ls = rt<<1,rs = rt<<1|1;
		s[ls].head = s[rs].head = s[rt].head;
		s[ls].d = s[rs].d = s[rt].d;
		int mid = (l+r)>>1;
		s[ls].sum = Get(l,mid,s[rt].head,s[rt].d);
		s[rs].sum = Get(mid+1,r,s[rt].head,s[rt].d);
		s[rt].head = 0;
	}
}
void update(int l,int r,int rt,int hd)
{
	if(L<=l&&r<=R){
		s[rt].head = hd;
		s[rt].d = a[hd];
		s[rt].sum = Get(l,r,hd,a[hd]);
		return ;
	}
	push_down(l,r,rt);
	int mid = (l+r)>>1;
	if(L<=mid) update(lson,hd);
	if(R>mid) update(rson,hd);
	s[rt].sum = s[rt<<1].sum + s[rt<<1|1].sum; 
}
ll query(int l,int r,int rt)
{
	if(L<=l&&r<=R) return s[rt].sum;
	int mid = (l+r)>>1;
	push_down(l,r,rt);
	ll ans = 0;
	if(L<=mid) ans += query(lson);
	if(R>mid) ans += query(rson);
	return ans;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)  scanf("%lld",a+i);
	for(int i=1;i<n;i++) scanf("%lld",b+i);
	for(int i=n-1;i>=0;i--){
		suf[i] = suf[i+1] + (n-i)*b[i];
		c[i] = c[i+1] + b[i];
	}
	for(int i=2;i<=n;i++){
		int v = a[i] - a[i-1] - b[i-1];
		if(v) st.insert(pa(i,v));
	}
	build(1,n,1); 
	scanf("%d",&q);
	char c[5];
	int l,x;
	while(q--){
		scanf("%s%d%d",c,&l,&x);
		if(c[0]=='+'){
			auto it = st.lower_bound(pa(l,0));
			if(l!=1){
				if(it==st.end()||it->fi!=l)	st.insert(pa(l,x));
				else{
					pa now = *it;
					st.erase(it),st.insert(pa(l,x+now.se));
				}
				it = st.lower_bound(pa(l,0)),it++;
			}
			L = R = l,a[l] = query(1,n,1) + x;
			while(it!=st.end()&&x>it->se) 
			x -= it->se,st.erase(it++);
			if(it!=st.end()){
				 pa now = *it;
				 st.erase(it);
				 st.insert(pa(now.fi,now.se-x));
				 R = now.fi - 1;
			}else R = n;
			update(1,n,1,l);  
		}else L = l,R = x,printf("%lld\n",query(1,n,1));
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值