您的位置:首页 > 其它

2016"百度之星" - 初赛(Astar Round2B)题解

2016-05-23 10:52 232 查看

1001

我们定义“区间的价值”为一段区间的最大值*最小值。

一个区间左端点在LL,右端点在RR,那么该区间的长度为(R-L+1)(R−L+1)。我们想要知道的是,对于长度为1-n的区间,最大价值的区间价值分别是多少。

要输出n种长度的区间的解,所以暴力是肯定不行的。我们要考虑到几个性质。

1.小区间的价值必然大于等于大区间,为什么呢,这是显然的嘛,只是不容易想到而已233

2.大区间的解可以由小区间推导而出,为什么呢,这是显然的嘛(233),其实是如果你得到了几个长度为i的区间,然后你在长度为i的区间的端点边上再加值,得到几个长度i+1的区间,那么i+1的最优解肯定是由长度i的区间推导出来的。

考虑到上面这两个性质,可以想到如果你把每个数字的值和原来在数组里面的位置记录之后按照值从大到小排序,然后从大到小往原来的数组里面放,如果它是孤立一个放在那里,就可以更新长度为1的区间的最大价值,因为值是按照从大到小放的,所以可以不需要维护最小值,只需要维护区间的最大值,可以把最大值记录在区间的端点上,然后你每次插入一个值的时候,检查它左右两边的位置是否都已经被使用了,然后取左右的最大值,乘上它,就是左右长度相加再加1的区间的价值,然后记录

但是这样操作之后,并不是所有长度的区间都能算出来,比如你5 4 1 2 3 ,你是左右两个长度为2的区间的价值先算出来,然后直接算出长度为5的区间,长度为3,4的区间的答案就没有得到,这时候怎么办呢,这时候就需要用到上面说的性质1了。因为长度3,4的区间的价值必定大于等于5的,并且你取长度3,4的时候,必定会取到长度5里面的最小值,然后也能取到5里面的最大值(想一想,这是肯定的,并且不可能取到更大的最大值,也不可能取到更大的最小值),所以

ans[4]=max(ans[4],ans[5])

ans[3]=max(ans[3],ans[4])

所以就能很愉快的得到所有长度区间的解了

代码:

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")

using namespace std;
#define   MAX           100005
#define   MAXN          6005
#define   maxnode       15
#define   sigma_size    30
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair<int,int>
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
//const double inf   = 1e18;
const double eps   = 1e-8;
const LL    mod    = 1e9+7;
const ull    mx    = 133333331;

/*****************************************************/
inline void RI(int &x) {
char c;
while((c=getchar())<'0' || c>'9');
x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/

struct Node{
int val,pos;
bool operator < (const Node&a)const{
return val>a.val;
}
}p[MAX];
LL ans[MAX];
int vis[MAX];
int maxv[MAX];

int main(){
int n;
while(cin>>n){
for(int i=0;i<n;i++){
int a;
scanf("%d",&a);
p[i]=(Node){a,i};
}
sort(p,p+n);
mem(ans,0);
mem(vis,0);
mem(maxv,0);
for(int i=0;i<n;i++){
int l=p[i].pos;
int r=p[i].pos;
int maxn=p[i].val;
if(l>0&&vis[l-1]){
maxn=max(maxn,maxv[l-1]);
l=l-vis[l-1];
}
if(r<n-1&&vis[r+1]){
maxn=max(maxn,maxv[r+1]);
r=r+vis[r+1];
}
ans[r-l+1]=max(ans[r-l+1],(LL)maxn*p[i].val);
vis[p[i].pos]=vis[r]=vis[l]=r-l+1;
maxv[r]=maxv[l]=maxn;
}
for(int i=n-1;i>0;i--) ans[i]=max(ans[i],ans[i+1]);
for(int i=1;i<=n;i++) printf("%I64d\n",ans[i]);
}
return 0;
}


1003

按照题意可以自己写一下每个格子到达终点的方案数,然后发现有点像杨辉三角,然后把图案倒过来斜过来看好像是个杨辉三角,并且推公式得到解是C(n+m−4,m−2),这就很好办了,预处理阶乘,然后快速幂求逆元求组合数

代码:

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")

using namespace std;
#define   MAX           200005
#define   MAXN          6005
#define   maxnode       15
#define   sigma_size    30
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair<int,int>
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
//const double inf   = 1e18;
const double eps   = 1e-8;
const LL    mod    = 1e9+7;
const ull    mx    = 133333331;

/*****************************************************/
inline void RI(int &x) {
char c;
while((c=getchar())<'0' || c>'9');
x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/

LL fact[MAX];
void init(){
fact[0]=1;
for(int i=1;i<=200000;i++) fact[i]=fact[i-1]*i%mod;
}
LL qpow(LL a,LL n){
LL ret=1;
while(n){
if(n&1) ret=ret*a%mod;
a=a*a%mod;
n>>=1;
}
return ret;
}

int main(){
int n,m;
init();
while(cin>>n>>m){
int row=n+m-3;
int col=max(n-1,m-1);
col=row-(col-1);
int a=n+m-4;
int b=m-2;
//cout<<fact[a]<<fact[b]<<endl;
cout<<(fact[a]*qpow(fact[b],mod-2)%mod)*qpow(fact[a-b],mod-2)%mod<<endl;
}
return 0;
}


1005

小A有一个含有n个非负整数的数列与m个区间。每个区间可以表示为[l,r]

它想选择其中k个区间, 使得这些区间的交的那些位置所对应的数的和最大。

例如样例中,选择[2,5]与[4,5]两个区间就可以啦。

这题的话可以把区间的左右端点都记录下来,然后扫过去,如果是左端点就加入一个数据结构中,如果是右端点,就看当前被覆盖的层数是否大于等于k,如果满足,就去数据结构中找第k大的那个左端点,然后前缀和减一下就好了,然后再在数据结构中删除这个右端点对应的左端点,这个数据结构可以使用线段树来维护

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")

using namespace std;
#define   MAX           200005
#define   MAXN          6005
#define   maxnode       15
#define   sigma_size    30
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair<int,int>
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
//const double inf   = 1e18;
const double eps   = 1e-8;
const LL    mod    = 1e9+7;
const ull    mx    = 133333331;

/*****************************************************/
inline void RI(int &x) {
char c;
while((c=getchar())<'0' || c>'9');
x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/

int a[MAX];
LL sum[MAX];
struct Edge{
int k;
int flag;
int pre;
bool operator < (const Edge&a) const{
if(k==a.k) return flag>a.flag;
return k<a.k;
}
}p[MAX];
int maxv[MAX<<2];
void build(int l,int r,int rt){
maxv[rt]=0;
if(l==r) return ;
middle;
build(lson);
build(rson);
}
void pushup(int rt){
maxv[rt]=maxv[lrt]+maxv[rrt];
}
void update(int l,int r,int rt,int pos,int d){
if(l==r){
maxv[rt]+=d;
return ;
}
middle;
if(pos<=m) update(lson,pos,d);
else update(rson,pos,d);
pushup(rt);
}

int query(int l,int r,int rt,int d){
if(l==r) return l;
middle;
if(d>maxv[lrt]) return query(rson,d-maxv[lrt]);
else return query(lson,d);
}
int main(){
int n,kk,m;
while(cin>>n>>kk>>m){
sum[0]=0;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
int tot=0;
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
p[tot++]=(Edge){x,1,0};
p[tot++]=(Edge){y,0,x};
}
sort(p,p+tot);
int tmp=0;
LL maxn=0;
build(1,n,1);
for(int i=0;i<tot;i++){
if(p[i].flag){
tmp++;
update(1,n,1,p[i].k,1);
}
if(p[i].flag==0){
if(tmp>=kk){
int pos=query(1,n,1,kk)-1;
maxn=max(maxn,sum[p[i].k]-sum[pos]);
}
update(1,n,1,p[i].pre,-1);
tmp--;
}
}
cout<<maxn<<endl;
}
return 0;
}


1006

中位数定义为所有值从小到大排序后排在正中间的那个数,如果值有偶数个,通常取最中间的两个数值的平均数作为中位数。

现在有n个数,每个数都是独一无二的,求出每个数在多少个包含其的区间中是中位数。

首先要发现这题每个数都是不同的,所以偶数区间是不可能的,所以每个数只有可能是奇数区间的中位数,然后数据量8000,给8s,可以用O(n2)的复杂度过去,对于每个a[i],考虑他前面比它小的数字记为-1,大于等于的数字记为1,然后前缀和,对于它后面的一个前缀f(r),去前面找前缀f(l),并且满足f(r)−f(l)=1,说明这个区间里面大于等于a[i]的个数比小于a[i]的个数多1个,所以说明a[i]是这个区间的中位数。

其次要注意因为是奇数区间,所以对于每个前缀,存的时候需要存一下它是奇数还是偶数,所以开数组的时候要开vis[val][2]

代码:

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")

using namespace std;
#define   MAX           16005
#define   MAXN          6005
#define   maxnode       15
#define   sigma_size    30
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair<int,int>
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
//const double inf   = 1e18;
const double eps   = 1e-8;
const LL    mod    = 1e9+7;
const ull    mx    = 133333331;

/*****************************************************/
inline void RI(int &x) {
char c;
while((c=getchar())<'0' || c>'9');
x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/

int a[MAX];
int f[MAX];
int vis[MAX][2];
int main(){
int n;
while(cin>>n){
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
mem(vis,0);
f[0]=8000;
vis[f[0]][0]++;
for(int j=1;j<i;j++){
if(a[j]>=a[i]) f[j]=f[j-1]+1;
else f[j]=f[j-1]-1;
vis[f[j]][j&1]++;
}
LL ret=0;
for(int j=i;j<=n;j++){
if(a[j]>=a[i]) f[j]=f[j-1]+1;
else f[j]=f[j-1]-1;
ret+=(LL)vis[f[j]-1][(j-1)&1];
}
//cout<<ret<<endl;
printf("%I64d",ret);
if(i==n) printf("\n");
else printf(" ");
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: