51nod百度之星2016练习赛

今天看了看51nod发现有这样一个练习赛,就做了做。因为实力太弱想不出E题,各位神犇勿D。

A 区间交

不难发现若干线段[li,ri]的交就是[max(li),min(ri)],那么我们考虑枚举min(ri),将ri>=min(ri)的区间按顺序加入,这时我们显然应该选第k小的li来更新答案。这些操作用个堆就可以轻松维护了。

时间复杂度为O(NlogN)。

#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
const int BufferSize=1<<16;
char buffer[BufferSize],*head,*tail;
inline char Getchar() {
	if(head==tail) {
		int l=fread(buffer,1,BufferSize,stdin);
		tail=(head=buffer)+l;
	}
	return *head++;
}
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘;
    return x*f;
}
typedef long long ll;
const int maxn=100010;
int n,k,m;
struct Line {
	int l,r;
	bool operator < (const Line& ths) const {return r>ths.r;}
}A[maxn];
priority_queue<int> Q;
ll S[maxn];
int main() {
	n=read();k=read();m=read();
	rep(i,1,n) S[i]=S[i-1]+read();
	rep(i,1,m) A[i].l=read(),A[i].r=read();
	sort(A+1,A+m+1);
	rep(i,1,k) Q.push(A[i].l);
	ll ans=max(0ll,S[A[k].r]-S[Q.top()-1]);
	rep(i,k+1,m) {
		if(Q.top()>A[i].l) Q.pop(),Q.push(A[i].l);
		ans=max(ans,S[A[i].r]-S[Q.top()-1]);
	}
	printf("%lld\n",ans);
	return 0;
}

B 中位数计数

开始没有看清题目:“求出每个数在多少个包含其的区间中是中位数”,所以我们只需考虑长度为奇数的区间就可以了。

那么对于每个数x求出答案就是一个经典问题了,<x的Ai设为-1,=x的Ai设为0,>x的Ai设为1,然后求一下有多少区间和为0就可以了。

时间复杂度为O(N^2)。

#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
const int BufferSize=1<<16;
char buffer[BufferSize],*head,*tail;
inline char Getchar() {
	if(head==tail) {
		int l=fread(buffer,1,BufferSize,stdin);
		tail=(head=buffer)+l;
	}
	return *head++;
}
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘;
    return x*f;
}
const int maxn=8010;
int n,A[maxn],B[maxn],S[maxn*2];
int main() {
	n=read();
	rep(i,1,n) A[i]=read();
	rep(i,1,n) {
		rep(j,1,n) {
			if(A[i]==A[j]) B[j]=0;
			else if(A[i]>A[j]) B[j]=1;
			else B[j]=-1;
			B[j]+=B[j-1];
		}
		int ans=0;
		memset(S,0,sizeof(S));
		for(int j=1;j<=n;j+=2) {
			S[B[j-1]+n]++;
			ans+=S[B[j]+n];
		}
		memset(S,0,sizeof(S));
		for(int j=2;j<=n;j+=2) {
			S[B[j-1]+n]++;
			ans+=S[B[j]+n];
		}
		printf("%d%c",ans,i==n?‘\n‘:‘ ‘);
	}
	return 0;
}

C 瞬间移动

f[i][j]=ΣΣf[i`][j`](i`>=i+1,j`>=j+1)

f[i+1][j]=ΣΣf[i`][j`](i`>i+1,j`>=j+1)

f[i][j+1]=ΣΣf[i`][j`](i`>=i+1,j`>j+1)

f[i][j]=f[i+1][j]+f[i][j+1]-{ΣΣf[i`][j`](i`>i+1,j`>j+1)}+f[i+1][j+1]

=f[i+1][j]+f[i][j+1]

然后坐标变换一下就转化成f[i][j]=f[i-1][j-1]+f[i-1][j]了,答案即为C(n+m-4,n-2)。

#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
const int BufferSize=1<<16;
char buffer[BufferSize],*head,*tail;
inline char Getchar() {
	if(head==tail) {
		int l=fread(buffer,1,BufferSize,stdin);
		tail=(head=buffer)+l;
	}
	return *head++;
}
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘;
    return x*f;
}
typedef long long ll;
const int maxn=1010;
const int mod=1000000007;
ll pow(ll n,int m) {ll ans=1;for(;m;m>>=1,(n*=n)%=mod) if(m&1) (ans*=n)%=mod;return ans;}
int f[maxn][maxn];
ll C(int n,int m) {
	ll ans1=1,ans2=1;
	rep(i,n-m+1,n) (ans1*=i)%=mod;
	rep(i,1,m) (ans2*=i)%=mod;
	return ans1*pow(ans2,mod-2)%mod;
}
int main() {
	int n=read()-1,m=read()-1;
	printf("%lld\n",C(n+m-2,n-1));
	return 0;
}

D 区间的价值

对随机数据有这样一个性质:如果从位置1向后跳,每次跳到下一个值严格比这一个大的位置,期望跳O(logn)次就会终止。

所以我们可以枚举左端点,模拟右端点的跳跃同时维护当前[l,r]的Ai的最大值mx与最小值mn,用二分套ST表就可以O(logn)计算出下一个mx或mn值改变的位置,在这个区间内答案是不变的,只要在区间打上一个max标记就可以了,用线段树可以做到O(logn),因为总跳越次数为O(nlogn),时间复杂度为O(nlog^2n)。

但这道题有点卡常数,进一步观察得到答案是随长度不增的,所以更新答案时只需要前缀打个标记就行了,这样就可以省掉线段树的常数了。

#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
const int BufferSize=1<<16;
char buffer[BufferSize],*head,*tail;
inline char Getchar() {
	if(head==tail) {
		int l=fread(buffer,1,BufferSize,stdin);
		tail=(head=buffer)+l;
	}
	return *head++;
}
inline int read() {
    int x=0,f=1;char c=Getchar();
    for(;!isdigit(c);c=Getchar()) if(c==‘-‘) f=-1;
    for(;isdigit(c);c=Getchar()) x=x*10+c-‘0‘;
    return x*f;
}
typedef long long ll;
const int maxn=100010;
const int inf=2000000000;
int n,A[maxn];
int Log[maxn],minv[20][maxn],maxv[20][maxn];
void init() {
	Log[0]=-1;rep(i,1,n+1) Log[i]=Log[i>>1]+1;
	rep(i,1,n) minv[0][i]=maxv[0][i]=A[i];
	minv[0][n+1]=-inf;maxv[0][n+1]=inf;
	for(int j=1;(1<<j)<=n+1;j++)
		for(int i=1;i+(1<<j)<=n+2;i++) {
			minv[j][i]=min(minv[j-1][i],minv[j-1][i+(1<<j-1)]);
			maxv[j][i]=max(maxv[j-1][i],maxv[j-1][i+(1<<j-1)]);
		}
}
int query(int l,int r,int t) {
	int k=Log[r-l+1];
	if(!t) return min(minv[k][l],minv[k][r-(1<<k)+1]);
	return max(maxv[k][l],maxv[k][r-(1<<k)+1]);
}
int getmn(int l,int val) {
	int r=n+1,mid,p=l;
	while(l<r) if(query(p,mid=l+r>>1,0)<val) r=mid; else l=mid+1;
	return l;
}
int getmx(int l,int val) {
	int r=n+1,mid,p=l;
	while(l<r) if(query(p,mid=l+r>>1,1)>val) r=mid; else l=mid+1;
	return l;
}
ll ans[maxn];
int main() {
	n=read();rep(i,1,n) A[i]=read();
	init();int cnt=0;
	rep(l,1,n) {
		int mx=A[l],mn=A[l],r=l;
		while(r<=n) {
			int p=min(getmn(r+1,mn),getmx(r+1,mx));
			cnt++;ans[p-l]=max(ans[p-l],(ll)mx*mn);
			r=p;mn=min(mn,A[r]);mx=max(mx,A[r]);
		}
	}
	dwn(i,n,1) ans[i]=max(ans[i],ans[i+1]);
	rep(i,1,n) printf("%lld\n",ans[i]);
	return 0;
}

F 货物运输

设传送站分别设在x和y,且x<y。

考虑二分答案ans,将所有R[i]-L[i]>ans的路线拿出来,那么|L[i]-x|+|R[i]-y|<=ans。

将所有的(L[i],R[i])作为平面的点,那么问题就转化成在平面中寻找一个点P,满足P到其他的点的曼哈顿距离均<=ans。

坐标变换一下(将(x,y)变换成(x-y,x+y))将曼哈顿距离转化成切比雪夫距离,然后直接维护矩形交就行了。

时间复杂度为O(NlogN)。

#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
const int BufferSize=1<<16;
char buffer[BufferSize],*head,*tail;
inline char Getchar() {
	if(head==tail) {
		int l=fread(buffer,1,BufferSize,stdin);
		tail=(head=buffer)+l;
	}
	return *head++;
}
inline int read() {
    int x=0,f=1;char c=Getchar();
    for(;!isdigit(c);c=Getchar()) if(c==‘-‘) f=-1;
    for(;isdigit(c);c=Getchar()) x=x*10+c-‘0‘;
    return x*f;
}
const int maxn=500010;
const int inf=1e9;
int n,m,L[maxn],R[maxn];
int check(int d) {
	int x1=-inf,y1=-inf,x2=inf,y2=inf;
	rep(i,1,m) if(R[i]-L[i]>d) {
		int X1=L[i]-R[i]-d,Y1=L[i]+R[i]-d,X2=L[i]-R[i]+d,Y2=L[i]+R[i]+d;
		if(x2<X1||x1>X2) return 0;
		else x1=max(x1,X1),x2=min(x2,X2);
		if(y2<Y1||y1>Y2) return 0;
		else y1=max(y1,Y1),y2=min(y2,Y2);
	}
	return 1;
}
int main() {
	n=read();m=read();int l=0,r=n,mid;
	rep(i,1,m) {
		L[i]=read();R[i]=read();
		if(L[i]>R[i]) swap(L[i],R[i]);
	}
	while(l<r) if(check(mid=l+r>>1)) r=mid; else l=mid+1;
	printf("%d\n",l);
	return 0;
}

  

时间: 2024-12-13 05:27:43

51nod百度之星2016练习赛的相关文章

百度之星2016资格赛D,水题

很简单的题,主要是要用字符串哈希,把字符串处理成整数.接下来可以继续用hash,也可以像我一样用个map就搞定了. /* * Author : ben */ #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <ctime> #include <iostream> #include <algorithm> #in

百度之星2016初赛之部分题解

All X Accepts: 1281 Submissions: 7580 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Problem Description F(x, m)F(x,m) 代表一个全是由数字xx组成的mm位数字.请计算,以下式子是否成立: F(x,m)\ mod\ k\ \equiv \ cF(x,m) mod k ≡ c Input 第一行一个整数TT,表示TT

2016百度之星 补题记

2016"百度之星" - 复赛(Astar Round3) 拍照 思路:先把所有的线段投影到x轴,然后将所有线段的起末坐标存进数组,排序后从坐标最小开始枚举.如果遇到起点标志,就加一:结束点标志减一.如此即可求出同一时刻,遇到当前线段结束点时,有多少线段包含在内 /************************************************************** Problem:hdu 5417 User: youmi Language: C++ Result

2016百度之星复赛 1003 拍照 优先队列

2016"百度之星" - 复赛(Astar Round3) Ended 2016-05-29 14:00:00 - 2016-05-29 17:00:00 Current Time: 00:46:02 Solved Pro.ID Title Ratio(Accepted / Submitted) Language   1001 D++游戏 13.79% (16/116) 中文   1002 K个联通块 17% (136/800) 中文 1003 拍照 22.49% (434/1930)

2016&quot;百度之星&quot; - 资格赛(Astar Round1) Problem D 简单题

Problem D Accepts: 1527 Submissions: 4307 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Problem Description 度熊所居住的 D 国,是一个完全尊重人权的国度.以至于这个国家的所有人命名自己的名字都非常奇怪.一个人的名字由若干个字符组成,同样的,这些字符的全排列的结果中的每一个字符串,也都是这个人的名字.例如,如果一个人名字

2016&quot;百度之星&quot; - 初赛(Astar Round2A)解题报告

此文章可以使用目录功能哟↑(点击上方[+]) 有点智商捉急,第一题卡了好久,看来不服老,不服笨是不行的了...以下是本人目前的题解,有什么疑问欢迎提出 链接→2016"百度之星" - 初赛(Astar Round2A)  Problem 1001 All X Accept: 0    Submit: 0 Time Limit: 2000/1000 mSec(Java/Others)    Memory Limit : 65536 KB  Problem Description F(x,

HDU 5701 中位数计数( 2016&quot;百度之星&quot; - 初赛(Astar Round2B) 思维 + 暴力)

传送门 中位数计数 Time Limit: 12000/6000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 852 Accepted Submission(s): 335 Problem Description 中位数定义为所有值从小到大排序后排在正中间的那个数,如果值有偶数个,通常取最中间的两个数值的平均数作为中位数. 现在有n个数,每个数都是独一无二的,求出每个数在多少个包含

hdu 5685 Problem A(2016&quot;百度之星&quot; - 资格赛(Astar Round1)——线段树)

题目链接:acm.hdu.edu.cn/showproblem.php?pid=5685 Problem A Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 564    Accepted Submission(s): 236 Problem Description 度熊手上有一本字典存储了大量的单词,有一次,他把所有单词组成了一个很长

HDU 5698 瞬间移动 (2016&quot;百度之星&quot; - 初赛(Astar Round2B) 1003)

传送门 瞬间移动 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 140 Accepted Submission(s): 66 Problem Description 有一个无限大的矩形,初始时你在左上角(即第一行第一列),每次你都可以选择一个右下方格子,并瞬移过去(如从下图中的红色格子能直接瞬移到蓝色格子),求到第n行第m列的格子有几种方案