【bestcoder #32】 ABCD题解

博客介绍了bestcoder挑战中的三道题目:PM2.5问题通过排序解决;Negative and Positive (NP)问题利用哈希与链表判断和的存在性;Brackets问题应用了卡特兰数理论,通过几何问题转化解决括号排列问题。
摘要由CSDN通过智能技术生成

PM2.5


Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1346    Accepted Submission(s): 571


问题描述
目前,我们用PM2.5的含量来描述空气质量的好坏。一个城市的PM2.5含量越低,它的空气质量就越好。所以我们经常按照PM2.5的含量从小到大对城市排序。一些时候某个城市的排名可能上升,但是他的PM2.5的含量也上升了。这就意味着他的空气质量并没有改善。所以这样的排序方式是不合理的。为了使得排序合理,我们提出了一个新的排序方法。我们按照两个PM2.5含量测量的差值(第一次-第二次)来对他们按降序排序,如果有重复,按照第二次的测量值升序排序,如果还有重复,按照输入的顺序排序。
输入描述
多组测试数据(大概
   
   
    
    100
   
   组),每一组测试数据第一行先给出一个整数
   
   
    
    n
   
   ,代表有
   
   
    
    n
   
   个城市需要被排序。城市被从
   
   
    
    0
   
   
   
   
    
    n1
   
   标号。接下来
   
   
    
    n
   
   行,每一含有两个整数代表每一个城市第一次和第二次被测量的PM2.5值。第
   
   
    
    i
   
   行描述城市
   
   
    
    i1
   
   的信息。
请处理到文件末尾。
[参数说明]
所有整数都在
   
   
    
    [1,100]
   
   的范围内。
输出描述
对于每一个数据,输出排好序之后的城市ID。
输入样例
2
100 1
1 2
3
100 50
3 4
1 2
输出样例
0 1
0 2 1

直接sort排序,在cmp里面处理一下。


#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int n;
struct data
{
	int x,y,d,id;
}a[10005];
bool cmp(data a,data b)
{
	if (a.d==b.d)
	{
		if (a.y==b.y) return a.id<b.id;
		return a.y<b.y;
	}
	return a.d>b.d;
}
int main()
{
        while (scanf("%d",&n)!=EOF)
	{
		for (int i=1;i<=n;i++)
			scanf("%d%d",&a[i].x,&a[i].y),a[i].d=a[i].x-a[i].y,a[i].id=i;
		sort(a+1,a+1+n,cmp);
		for (int i=1;i<n;i++)
			cout<<a[i].id-1<<" ";
		cout<<a[n].id-1<<endl;
	}
	return 0;
}


B:

Negative and Positive (NP)


Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1161    Accepted Submission(s): 59


问题描述
给定一个数组
   
   
    
    (a0,a1,a2,an1)
   
   和一个整数
   
   
    
    K
   
   , 请来判断一下是否存在二元组
   
   
    
    (i,j)(0ij<n)
   
   使得 
   
   
    
    NPsum(i,j)
   
    刚好为
   
   
    
    K
   
   。这里
   
   
    
    NPsum(i,j)=aiai+1+ai+2++(1)jiaj
   
   
输入描述
多组测试数据。在文件的第一行给出一个
   
   
    
    T
   
   ,表示有
   
   
    
    T
   
   组数据。
在接下来的
   
   
    
    2T
   
   行里,将会给出每一组数据。
每一组数据占两行,第一行包含
   
   
    
    n
   
   
   
   
    
    K
   
   。
第二行包含
   
   
    
    (a0,a1,a2,an1)
   
   以一个空格分开。
[参数说明]
所有输入均为整数。

   
   
    
    0<T25,1n1000000,1000000000ai1000000000,1000000000K1000000000
   
   
输出描述
对于每一个数据,输出占一行,输出格式是Case #id: ans,这儿id是数据编号,从1开始,ans是根据是否找到满足的二元组而定为“Yes.” 或 “No.” (不包含引号)
看样例可以获得更多的信息。
输入样例
2
1 1
1
2 1
-1 0
输出样例
Case #1: Yes.
Case #2: No.
Hint
如果数据比较多,建议使用快速读入。

求出"前缀和",一加一减的。


我们考虑奇数位为正,偶数位为负的情况(反之考虑方法一样):

对于sum[i](i%2=0)如果i+1作第一位,能使得结果为k的话,一定存在sum[i]+k这个数,如何快速判断是否存在sum[i]+k呢?


我比赛用了map,结果mle+tle。。

其实set和hash+链表都可以过,代码是hash+链表。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <map>
#define LL long long
#define mod 1000007
using namespace std;
LL x[1000005];
struct edge
{
	LL v;
	int ne;
}e[1100000];
int a[1000005],h[1100000],tot=0,n,T,k;
void read(int &tmp)
{
	tmp=0;
	char ch=getchar();
	int fu=1;
	for (;ch<'0'||ch>'9';ch=getchar())
		if (ch=='-') fu=-1;
	for (;ch>='0'&&ch<='9';ch=getchar())
		tmp=tmp*10+ch-'0';
	tmp*=fu;
}
void Insert(LL x)
{
	int ha=x%mod;
	if (ha<0) ha=-ha;
	for (int i=h[ha];i;i=e[i].ne)
		if (e[i].v==x) return;
	e[++tot].ne=h[ha];
	h[ha]=tot;
	e[tot].v=x;
}
bool Find(LL x)
{
	int ha=x%mod;
	if (ha<0) ha=-ha;
	for (int i=h[ha];i;i=e[i].ne)
		if (e[i].v==x) return 1;
	return 0;
}
void Clear()
{
	for (int i=1;i<=n;i++)
	{
		int ha=x[i]%mod;
		if (ha<0) ha=-ha;
		h[ha]=0;
	}
	tot=0;
}
int main()
{
        read(T);
	for (int i=1;i<=T;i++)
	{
		read(n),read(k);
		x[0]=0;
		int f=0;
		for (int i=1;i<=n;i++)
		{
			read(a[i]);
			if (i&1) 
				x[i]=x[i-1]+a[i];
			else x[i]=x[i-1]-a[i];
		}
		for (int i=n;i;i--)
		{
			if (x[i]==k)
			{
				f=1;break;
			}
			if ((i&1)==0)
			{
				if (Find(x[i]+k)) {f=1;break;}
			}
			else if (Find(x[i]-k)) {f=1;break;}
			Insert(x[i]);
		}
		printf("Case #%d: ",i);
		if (f) printf("Yes.\n");
		else printf("No.\n");
		Clear();
	}
	return 0;
}


C:

Brackets


Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 215    Accepted Submission(s): 33


问题描述
我们给出下列递归的合法括号序列的定义:
  ● 空序列是合法括号序列
  ● 如果s是一个合法括号序列,那么(s)也是合法括号序列
  ● 如果a和b是合法括号序列,那么ab也是合法括号序列
  ● 没有其它情况是合法括号序列

比如下列括号序列是合法括号序列
(), (()), ()(), ()(())
下列括号序列则不是
(, ), )(, ((), ((()

现在,我们要构造长度为
   
   
    
    n
   
   的合法括号序列,前面的一些括号已经给出,问可以构造出多少合法序列。
输入描述
多组测试数据(大概
   
   
    
    2000
   
   ),每一组数据占两行。
第一行给出一个整数
   
   
    
    n
   
   。
第二行给出一个字符串代表前面已经确定的几个括号。
请处理到文件末尾。

[参数约定]

   
   
    
    1n1000000
   
   
字符串只包含’(’和’)’,长度大于
   
   
    
    0
   
   并且不超过
   
   
    
    n
   
   .
输出描述
对于每一个数据,在一行中输出答案对
   
   
    
    1000000007
   
   取余的结果。
输入样例
4
()
4
(
6
()
输出样例
1
2
2
Hint
第一组数据只有一种可能,它就是()().
对于第二组数据他有两种可能,他们是(()) 和()().
对于第三种数据,两种可能是()()() 和()(()).

卡特兰数的应用~


百度百科有详细讲解~


先考虑一个简化的问题,给出n个左括号,m个右括号,(n>m)求有多少种合法的排列。


把左括号看成1,右括号看成-1,然后问题就变成了求1,-1有多少种排列方法,使得前缀和始终>=0。


百度百科扩展部分有详解~我大概说一下思路


先考虑使得前缀和>0的情况,转化为几何问题;然后再考虑>=0的情况。


最后得出的答案是C(n+m-1,m)-C(n+m-1,m-1)


这道题可以先求出需要填a1个左括号,b1个右括号,相当于从(a1,b1)走到(n/2,n/2)不跨过对角线的方案数。


相当于求(0,0)到(n/2-a1,n/2-b1)不跨过对角线的方案数,直接代到上面的式子即可。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <string>
#define mod 1000000007
#define M 1000000+5
#define LL long long
using namespace std;
int n;
char s[M];
LL fac[M],inv[M];
LL Pow(LL a,int n)
{
	LL ans=1LL,base=a;
	while (n)
	{
		if (n&1) ans=ans*base%mod;
		base=base*base%mod;
		n>>=1;
	}
	return ans;
}
void Prepare()
{
	fac[0]=1LL;
	for (int i=1;i<=M-3;i++)
	{
		int x=i;
		fac[i]=fac[i-1]*(LL)x%mod;
	}
	inv[M-3]=Pow(fac[M-3],mod-2);
	for (int i=M-4;i>=0;i--)
		inv[i]=inv[i+1]*(i+1)%mod;
}
LL Getc(int n,int m)
{
	if (n<m) return 0LL;
	return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
	Prepare();
        while (scanf("%d",&n)!=EOF)
	{
		scanf("%s",s);
		int cnt=0,f=1,a=n/2,b=n/2,l=strlen(s);
		for (int i=0;i<l;i++)
		{
			if (s[i]=='(') cnt++,a--;
		        else cnt--,b--;
			if (cnt<0) 
			{
				f=0;
				break;
			}
		}
		if (!f||n%2==1||a<0||b<0)
		{
			puts("0");
			continue;
		}
		printf("%lld\n",(Getc(a+b,a)-Getc(a+b,a-1)+mod)%mod);
	}
	return 0;
}

(卡特兰数太神奇了~)


Equation


Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 49    Accepted Submission(s): 18


问题描述
Gorwin 对等式非常感兴趣。前几天他获得了这么一个等式

   
   
    
    x1+x2+x3++xn=n
   
   , 这里 
  
  
0xinfor1inxixi+1xi+1for1in1
对于一个给定的 n ,Gorwin想要知道有多少 xi 的组合满足上述等式。 由于结果比较大,输出答案对 m 取余的结果就行。
输入描述
多组测试数据。文件的第一行给出一个
   
   
    
    T
   
   ,表示下面有
   
   
    
    T
   
   组数据。下面
   
   
    
    T
   
   行每行给出两个整数
   
   
    
    n,m
   
   以一个空格分开。
[参数约定]

   
   
    
    1T<20,1n50000,1m1000000000
   
   
输出描述
对于每一个数据输出占一行,输出格式为Case #id: ans,这儿id是数据编号从1开始,ans是你输出的答案。
看样例得到更多信息。
输入样例
2
3 100
5 100
输出样例
Case #1: 2
Case #2: 3

这道题和【CF #286(div 2)】C一样,发现dp的转移可以缩减,然后O(nsqrt(n))出解。



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值