Codeforces #round 145 Div2 C题

12 篇文章 0 订阅
6 篇文章 1 订阅


Codeforces #round 145 Div2 C题

Scientists say a lot about the problems of global warming and cooling of the Earth. Indeed, such natural phenomena strongly influence all life on our planet.

Our hero Vasya is quite concerned about the problems. He decided to try a little experiment and observe how outside daily temperature changes. He hung out a thermometer on the balcony every morning and recorded the temperature. He had been measuring the temperature for the last n days. Thus, he got a sequence of numbers t1, t2, ..., tn, where the i-th number is the temperature on the i-th day.

Vasya analyzed the temperature statistics in other cities, and came to the conclusion that the city has no environmental problems, if first the temperature outside is negative for some non-zero number of days, and then the temperature is positive for some non-zero number of days. More formally, there must be a positive integer k (1 ≤ k ≤ n - 1) such that t1 < 0, t2 < 0, ..., tk < 0 and tk + 1 > 0, tk + 2 > 0, ..., tn > 0. In particular, the temperature should never be zero. If this condition is not met, Vasya decides that his city has environmental problems, and gets upset.

You do not want to upset Vasya. Therefore, you want to select multiple values of temperature and modify them to satisfy Vasya's condition. You need to know what the least number of temperature values needs to be changed for that.

Input

The first line contains a single integer n (2 ≤ n ≤ 105) — the number of days for which Vasya has been measuring the temperature.

The second line contains a sequence of n integers t1, t2, ..., tn (|ti| ≤ 109) — the sequence of temperature values. Numbers ti are separated by single spaces.

Output

Print a single integer — the answer to the given task.


这道题吧,当时比赛的时候,队友用的dp写的,过了,我当时用树状数组写的,没过,然后回来了又重新写了一遍,其实是可以写的,其实,如果做题比较多的同学,这道题一看就是一道简单题了。

然后我就用了两种方法写,其实思路是一样的,不过建议大家还是看我第二种解法吧,dp解法,真的是,简单的题竟然被我用复杂的方法来做。


线段树做法:


我的思路是这样的,从1-n扫描一遍,对于每个点,找出它的左侧比0大的,右侧比0小的,两个个数相加,存到ans【】这个数组里,最后从ans数组找最小的,就是答案

那么,如果用线段树做的话,只需要从1-n,每个点update( p[i] ),即让p[i[处的值+1,然后求大于0的个数,也就是query(0,n),这样就可以求得大于0的个数,这样通过1-n从左到右就可以求得每个点左侧 大于0的个数。再从n---1,求一下每个点右侧小于0的个数,最后相加。

另外就是有特殊情况,如果用线段树做的话需要特判,就是一个数全部是正,以及这一组数据,当时就是卡这一组数据

2

3 -5 这个正确答案是2,而我输出的是1,因为必须是开始负,后来正,所以要改2次

所以我最后改成,扫描1----n-1 这些点,根据n-1这个点更新第n个点


其实吧,感觉这道题用线段树做非常慢,而且比较麻烦,我之前还用了离散化处理,因为题目数的范围有点大,而且有负数,这样的题,还是乖乖用dp写好,线段树写只能算是赛后一个强化吧。


#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
int n;
const int maxn = 1e5+4;
struct spe
{
	int data,id;	
};
spe p[maxn];
int tree1[maxn<<2];
int tree2[maxn<<2];
int L,R;
bool cmp1( spe a, spe b )
{
	return a.data < b.data;
}
bool cmp2( spe a, spe b )
{
	return a.id < b.id;
}
void change( int rt, int tree[] )
{
	tree[rt] = tree[rt<<1] + tree[rt<<1|1];
}
void update1( int l,int r,int rt,int pos)
{
	if( l == r )
	{
		tree1[rt]++;
		return;	
	}
	int m = (l+r)>>1;
	if( pos <= m )
		update1(l,m,rt<<1,pos);
	else update1(m+1,r,rt<<1|1,pos);
	change(rt,tree1);
}
void update2( int l,int r,int rt,int pos)
{
	if( l == r )
	{
		tree2[rt]++;
		return;
	}
	int m = (l+r)>>1;
	if( pos <= m )
		update2(l,m,rt<<1,pos);
	else update2(m+1,r,rt<<1|1,pos);
	change(rt,tree2);
	
}
int getsum( int l,int r,int rt)
{
	if( l >= L && r <= R )
	{
		return tree1[rt];
	}
	int m = (l+r)>>1;
	int ans = 0;
	if( m >= L )
		ans += getsum(l,m,rt<<1);
	if( m < R )
		ans += getsum(m+1,r,rt<<1|1);
	return ans;	
}
int getsum2( int l,int r,int rt)
{
	if( l >= L && r <= R )
	{
		return tree2[rt];
	}		
	int m = (l+r)>>1;
	int ans = 0;
	if( m >= L )
		ans += getsum2(l,m,rt<<1);
	if( m < R )
		ans += getsum2(m+1,r,rt<<1|1);
	return ans;
}
int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	cin>>n;
	memset(p,0,sizeof p);
	memset(tree1,0,sizeof tree1);
	memset(tree2,0,sizeof tree2);
	p[0].data = 0;
	p[0].id = 0;
	for( int i = 1 ; i <= n ; i++ )
	{
		scanf("%d",&p[i].data);
		p[i].id = i;
	} 
	sort(p,p+n+1,cmp1);
	int cnt = 1;
	int flag = p[0].data;
	for( int i = 0 ; i <= n ; i++ )
		if( p[i].data == flag )
			p[i].data = cnt;
		else
		{
			flag = p[i].data;
			p[i].data = ++cnt;
		}
	sort(p,p+n+1,cmp2);
	int ans[maxn];
	memset(ans,0,sizeof ans);
	L = p[0].data;
	R = n;
	for( int i = 1 ; i < n ; i++ )
	{
		update1(1,n,1,p[i].data);
		ans[i] += getsum(1,n,1);
	}

	L = 1;
	R = p[0].data;
	update2(1,n,1,p[n].data);
	for( int i = n-1 ; i >= 1 ; i-- )
	{
		ans[i] += getsum2(1,n,1);
		update2(1,n,1,p[i].data);
	}
	
	ans[n] = p[n].data >= p[0].data? ans[n-1]:ans[n-1]+1;	//最后一个数的特判
	
	
	int aa = inf;
	for( int i = 1 ; i <= n ; i++ )
		aa = min(aa,ans[i]);
	cout<<aa<<endl;
	
	
	/* 
	for( int i = 1 ; i <= n ; i++ )
		cout<<ans[i]<<" ";
    */
	return 0;
}

dp的思路其实和我上边线段树的思路一样的,统计每个点左侧大于等于0的个数,统计每个点右侧小于等于0的个数,然后相加。

dp 代码:

#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
int n;
const int maxn = 1e5+4;


int main()
{
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	cin>>n;
	int a[maxn];
	int dp[maxn];
	memset(dp,0,sizeof dp);
	for( int i = 1 ; i < n ; i++ )
	{
		cin>>a[i];
		if( a[i] >= 0 )
			dp[i] = dp[i-1]+1;
		else dp[i] = dp[i-1];
	}
	cin>>a[n];
	dp[n] = a[n]<=0? dp[n-1]+1:dp[n-1];	//最后一个的特判
	int dp2[maxn];
	memset(dp2,0,sizeof dp2);
	for( int i = n-1 ; i >= 1 ; i-- )
		if( a[i+1] <= 0 )
			dp2[i] = dp2[i+1]+1;
		else dp2[i] = dp2[i+1];
	int ans = inf;
	for( int i = 1 ; i <= n ; i++ )
		ans = min(ans,dp[i]+dp2[i]);
	cout<<ans<<endl;
	return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值