绍兴一中模拟赛5.26

yk出的题真是坑,这么难的题让我们来做
T1:新田忌赛马
【问题描述】
(注:此题为d2t2-难度)
田忌又在跟大王van赛马的游戏
田忌与大王一共有2n匹马,每个马都有一个能力值x,1<=x<=2n且每匹马的x互不相同。每次田忌与大王放出一匹马,较大的获胜。但是田忌有一个能力,在任何比赛的开始前,他可以把马变成x较小的获胜,并一直持续到比赛结束
田忌可以一直不用这个能力,也可以在第一轮前使用
现在,田忌已经知道了大王的出马顺序,田忌要问聪明的你,他最多能获得几次胜利?
【输入格式】
第一行为一个整数:N(1<=N<=50000)接下来 一行n个数,为大王的顺序出场的n匹马的能力值(田忌的马可以通过此求出)
【输出格式】
一个整数,表示最多的获胜次数
【样例输入】
4
1
8
4
3
【样例输出】
3

【样例说明】
田忌第一次出能力为7的马获胜
第二次开始前使用能力,出能力为6的马获胜
第三次出能力为5的马失败
第四次出能力为2的马获胜
总共3次
【出题人的关怀】
乱搞出奇迹(雾)
大胆猜想,不要证明
【数据规模】
对于20%的数据,n<=10
对于40%的数据 n<=20
对于35%的数据,不使用能力也可获得最多胜利(即20个点中有7个点不使用能力的程序能过(雾))
前3个档的总分为60分(出题人的关怀)
对于80%的数据,n<=5000
对于100%的数据,n<=50000,
#题解:
贪心,令f[i]表示前i个每次都出比对方稍微大一点的牌,最多能赢几次
g[i]表示从i-n中每次出比对方稍微小一点的牌,最多赢几次。
ans=max(f[i]+g[i+1]) 0<=i<=n
#标程:

#include<bits/stdc++.h>
using namespace std;
const int N=50003;
int n,a[N],f[N],vis[N*2],i,ans,g[N];
set<int>s1,s2;
set<int>::iterator it;
int read(){
	int x=0,f=1;char c;
	do{c=getchar();if (c=='-') f=-1;}while (c<'0'||c>'9');
	while ('0'<=c && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*f;
}
int main(){
	n=read();
	for (i=1;i<=n;i++) a[i]=read(),vis[a[i]]=1;
	for (i=1;i<=2*n;i++)
		if (!vis[i]) s1.insert(i),s2.insert(-i);
	for (i=1;i<=n;i++){
		it=s1.lower_bound(a[i]);
		f[i]=f[i-1];
		if (it!=s1.end()) f[i]++,s1.erase(it); 
	}
	for (i=n;i;i--){
		it=s2.lower_bound(-a[i]);
		g[i]=g[i+1];
		if (it!=s2.end()) g[i]++,s2.erase(it);
	}
	for (i=0;i<=n;i++) ans=max(ans,f[i]+g[i+1]);
	printf("%d",ans);
}

T2:会议(cf854D Jury Meeting)
【问题背景】
yukuai26准备出一道水题,但是他不知道想出啥,于是召集了一堆人开会
【问题描述】
开会当然需要在一个特定的城市开会,我们规定他们为0城市,这n个人在不同的1-n的城市中,他们需要乌鸦坐飞机才能到达0号城市。
有一堆飞机航线,只有2种航线,一种从0到其他城市,一种从其他城市到0,飞机有d[i],f[i],t[i],c[i]表示时间,起点,终点,和花费。每个人需要从他们的城市出发,所有人在0号城市待上k天开会,最后返回。注意,开会的开始第一天和最后一天是不能坐飞机来到或离开0号城市的
那么,你能来帮他们算算他们最少共需要多少钱吗?
如果不能满足要求,请输出-1(你觉得会有多少分呢)
【输入格式】
第一行一个正整数 n,m,k表示有0-n这些城市,m条飞机线,k天的开会时间
第2-m+1行为 每行4个整数
【输出格式】
1个数,如果有解输出最小的花费,无解输出-1
【样例输入1】
1 2 10
20 0 1 36
10 1 0 28
【样例输出1】
-1
【样例输入2】
2 5 5
1 1 0 1
2 2 0 100
3 2 0 10
9 0 1 1000
10 0 2 10000
【样例输出2】
11011

【数据规模】
对于20%的数据,n,m<=10
对于40%的数据,n<=100 m<=3000
对于另20%的数据,c[i],d[i]<=1
对于另20%的数据,d[i]为i*2-1且k为1
对于100%的数据,n,m<=1e5,k<=1e6 f[i],t[i]<=n ,1<=c[i],d[i]<=1e6
【出题人的关怀】
难度为d2t1哦,暴力80还是很容易的
#题解:
这题神坑,我刚开始一直不过,后来把变量名的定义顺序按照标程来定义就A了,玄学。。。
我们考虑维护2个数组f[i],g[i],f[i]表示在i时间前所有人到0号节点的最小花费,如果不能为inf,g[i]表示在i时间后所有人从0号节点回家的最小化费,如果不能为inf
对于每个f[i],通过二分找到一个最小的g[j]
使得他们之间至少相差k
然后ans取min即可
#标程:

#include<bits/stdc++.h>
typedef long long ll;
const int N=200005;
using namespace std;
ll read(){
	ll x=0,f=1;char c;
	do{c=getchar();if (c=='-') f=-1;}while (c<'0'||c>'9');
	while ('0'<=c && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*f;
}
int i,l,r,mid,n,m,k,num;
ll sum,vis[N],f[N],g[N],ans;
struct node{
	ll d,f,t,c;
}z[N];
bool cmp(node x,node y){
	return x.d<y.d;
}
int main(){
	n=read();m=read();k=read();
	for (i=1;i<=m;i++) z[i].d=read(),z[i].f=read(),z[i].t=read(),z[i].c=read();
	sort(z+1,z+m+1,cmp);
	for (i=1;i<=m;i++){
		if (z[i].f){
			sum-=vis[z[i].f];
			if (!vis[z[i].f]) num++,vis[z[i].f]=z[i].c;
			vis[z[i].f]=min(vis[z[i].f],z[i].c);
			sum+=vis[z[i].f];
		}
		if (num==n) f[i]=sum;
		else f[i]=-1;
	}
	memset(vis,0,sizeof(vis));
	num=sum=0;
	for (i=m;i;i--){
		if (z[i].t){
			sum-=vis[z[i].t];
			if (!vis[z[i].t]) num++,vis[z[i].t]=z[i].c;
			vis[z[i].t]=min(vis[z[i].t],z[i].c);
			sum+=vis[z[i].t];
		}
		if (num==n) g[i]=sum;
		else g[i]=-1;
	}
	ans=-1;
	for (i=1;i<=m;i++){
		if (f[i]==-1) continue;
		l=i+1,r=m;
		while (l<r){
			mid=(l+r)>>1;
			if (z[mid].d<=z[i].d+k) l=mid+1;
			else r=mid;
		}
		if (z[l].d<=z[i].d+k) continue;//这句不能删
//当l==m时不会运行二分的部分,导致可能会不满足条件
		if (l>m || g[l]==-1) continue;
		if (ans==-1) ans=f[i]+g[l];
		else ans=min(ans,f[i]+g[l]);
	}
	printf("%I64d",ans);
}

T3:将军(bzoj5249)
【问题描述】
手持倚天剑的将军wzp,有着很多士兵
Wzp有n个士兵,每个士兵有一个能力值,他正在与cqz与szb搏杀着,wzp准备把n个士兵排成一个列,要求第i个士兵的能力值不小于第i/k(下取整)个士兵的能力值。同时,wzp希望第一个士兵能力值尽可能大,如果相同,希望第二个士兵能力值尽可能大,如果相同。。以此类推。
简单题意:给定n,k,d数组,找出一个字典序最大的排列,使得d[i]>=dtrunc(i/k),trunc为下取整
【输入格式】
输入文件general.in共有两行,第一行为n和k(其中k为一个实数!!)
第二行n个数,为士兵的能力值
【输出格式】
输出一行n个数,为字典序最大的符合要求的排列

【样例输入】
4 2.0
114 514 1919 810 。

【输出样例】
114 810 514 1919

【出题人的关怀】
此题欢迎乱搞同学(吗?)
#题解:
大佬的博客
#标程:

#include<bits/stdc++.h>
using namespace std;
const int N=500003;
#define mid ((l+r)>>1)
struct kk{
	int val,lazy;
}tr[N<<2];
int a[N],cnt[N],size[N],fa[N],ans[N],i,n,x;
double k;
void wr(int x){if (x<0) {putchar('-');wr(-x);return;}if(x>=10)wr(x/10);putchar(x%10|48);}
void wri(int x){wr(x);putchar(' ');}
int read(){
    char c;int x=0,f=1;
	do{c=getchar();if(c=='-')f=-1;}while(c<48||c>57);
	do x=(x<<1)+(x<<3)+(c^48),c=getchar();while(c>=48&&c<=57);
    return f*x;
}
bool cmp(int x,int y){
	return x>y;
}
void down(int t){
	if (tr[t].lazy){
		int tmp=tr[t].lazy;
		tr[t<<1].val+=tmp;tr[t<<1].lazy+=tmp;
		tr[t<<1|1].val+=tmp;tr[t<<1|1].lazy+=tmp;
		tr[t].lazy=0;
	}
}
void up(int t){
	tr[t].val=min(tr[t<<1].val,tr[t<<1|1].val);
}
void build(int t,int l,int r){
	if (l==r){
		tr[t].val=l;
		return;
	}
	build(t<<1,l,mid);
	build(t<<1|1,mid+1,r);
	up(t);
}
void update(int t,int l,int r,int x,int y,int val){
	if (x<=l && r<=y){
		tr[t].val+=val;
		tr[t].lazy+=val;
		return;
	}
	down(t);
	if (x<=mid) update(t<<1,l,mid,x,y,val);
	if (mid<y) update(t<<1|1,mid+1,r,x,y,val);
	up(t);
}
int query(int t,int l,int r,int k){
	if (l==r) return k<=tr[t].val?l:l+1;
	down(t);
	if (k<=tr[t<<1|1].val) return query(t<<1,l,mid,k);
	return query(t<<1|1,mid+1,r,k);
}
int main(){
	n=read();scanf("%lf",&k);
	for (i=1;i<=n;i++) a[i]=read(),fa[i]=floor(i/k),size[i]=1;
	//floor删掉在bzoj上就过不了,洛谷上能过,不知道为什么
	sort(a+1,a+n+1,cmp);
	for (i=n;i;i--) cnt[i]=(cnt[i+1]+1)*(a[i]==a[i+1]),size[fa[i]]+=size[i];
	build(1,1,n);
	for (i=1;i<=n;i++){
		if (fa[i] && fa[i]!=fa[i-1]) update(1,1,n,ans[fa[i]],n,size[fa[i]]-1);
		ans[i]=x=query(1,1,n,size[i]);
		x+=cnt[x];cnt[x]++;x-=cnt[x]-1;
		update(1,1,n,x,n,-size[i]);
	}
	for (i=1;i<=n;i++) wri(a[ans[i]]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值