10.08NOIP模拟赛

                出纳员的雇佣

【题目描述】

德黑兰的一家每天24小时营业的超市,需要一批出纳员来满足它的需要。超市经理雇佣你来帮他解决他的问题——超市在每天的不同时段需要不同数目的出纳员(例如:午夜时只需一小批,而下午则需要很多)来为顾客提供优质服务。他希望雇佣最少数目的出纳员。

经理已经提供你一天的每一小时需要出纳员的最少数量——R0, R1, ..., R23。R0表示从午夜到上午1:00需要出纳员的最少数目,R1表示上午1:00到2:00之间需要的,等等。每一天,这些数据都是相同的。有N人申请这项工作,每个申请者I在没24小时中,从一个特定的时刻开始连续工作恰好8小时,定义tI(0≤tI≤23)为上面提到的开始时刻。也就是说,如果第I个申请者被录取,他(她)将从tI时刻开始连续工作8小时。

你将编写一个程序,输入RI(I = 0..23)和tI (I = 1..N),它们都是非负整数,计算为满足上述限制需要雇佣的最少出纳员数目。在每一时刻可以有比对应的RI更多的出纳员在工作。

【输入格式】

输入文件的第一行为测试点个数(<= 20)。每组测试数据的第一行为24个整数表示R0,R1,..., R23(RI≤1000)。接下来一行是N,表示申请者数目(0≤N≤1000),接下来每行包含一个整数tI(0≤tI≤23)。两组测试数据之间没有空行。

【输出格式】

对于每个测试点,输出只有一行,包含一个整数,表示需要出纳员的最少数目。如果无解,你应当输出“No Solution”。

【样例输入】

1

1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 1

5

0

23

22

1

10

【样例输出】

1

problem a

Description

一次考试共有n个人参加,第i个人说:“有ai个人分数比我高,bi个人分数比我低。”问最少有几个人没有说真话(可能有相同的分数)

Input

第一行一个整数n,接下来n行每行两个整数,第i+1行的两个整数分别代表ai、bi

Output

一个整数,表示最少有几个人说谎

Sample Input

3

2 0

0 2

2 2

Sample Output

1

HINT

100%的数据满足: 1≤n≤100000   0≤ai、bi≤n

 

借教室

【题目描述】

在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。

面对海量租借教室的信息,我们自然希望编程解决这个问题。

我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借。共有m份 订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租 借教室(包括第sj天和第tj天),每天需要租借dj个教室。

我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供dj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。

借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申 请人修改订单。这里的无法满足指从第sj天到第tj天中有至少一天剩余的教室数量不足dj个。

现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。

【输入格式】

第一行包含两个正整数n,m,表示天数和订单的数量。

第二行包含n个正整数,其中第i个数为ri,表示第i天可用于租借的教室数量。

接下来有m行,每行包含三个正整数dj,sj,tj,表示租借的数量,租借开始、结束分别在 第几天。

每行相邻的两个数之间均用一个空格隔开。天数与订单均用从1开始的整数编号。

【输出格式】

如果所有订单均可满足,则输出只有一行,包含一个整数 0。否则(订单无法完全满足) 输出两行,第一行输出一个负整数-1,第二行输出需要修改订单的申请人编号。

【样例输入】

 

4 3

2 5 4 3

2 1 3

3 2 4

4 2 4

【样例输出】

-1

2

【输入输出样例说明】

第 1 份订单满足后,4 天剩余的教室数分别为 0,3,2,3。第 2 份订单要求第 2 天到 第 4 天每天提供 3 个教室,而第 3 天剩余的教室数为 2,因此无法满足。分配停止,通知第 2 个申请人修改订单。

【数据范围】

对于 10%的数据,有1 ≤ n,m ≤ 10;

对于 30%的数据,有1 ≤ n,m ≤ 1000;

对于 70%的数据,有1 ≤ n,m ≤ 10^5;

对于 100%的数据,有1 ≤ n,m ≤ 10^6,0 ≤ ri,dj ≤ 10^9,1 ≤ sj ≤ tj ≤ n。



T1 出纳员的雇佣

二分答案 + spfa判断 +差分约束

考试的时候完全没有思路,想到了DFS,但是又不知道怎么搜,然后又想到最短路覆盖,还是不会打,于是0分了。

设dis[i]表示前i个时刻工作的人数 ned[i]表示i时刻至少有多少个人工作hav[i]表示i时刻我们可以雇佣到多少人工作 lim表示想要招募的最少的员工个数,可以得到几个方程:

             

       0<= d[1] <= ned[1];//显然刚开始不可能雇用超过ned[1]的人要不然不是最优解

       0<=d[i] – d[i-1]<=hav[i]//i时刻雇佣的工作人员不大于申请总人数但不小于0

       d[i]-d[i-8]>=ned[i]//i>=8时成立这是当8<=i<=24时

       d[i-8+24]-d[i]<=lim-ned[i]//i<8时前一天雇佣的人还可以上班,因此可以反面处理

       d[24]-d[0]>=lim//二十四小时内雇佣的人数不小于lim

 

然而lim是一个变量,应用以上模型只能判断一个lim是否合法,而不能找出最优解,spfa判定d[0] ==lim?之后二分答案求解

T2 problem a

       DP+ 递推

石家庄集训期中考试题……当时就不会,现在还不会,考试输出rand()%10000也是醉了

设f[i]表示名次区间取到右端点时最多的说真话人数,那么n-f[n]就是最少的说假话人数

读入a表示比他分数高的人,那么他名次左端点就是a+1,读入b表示比他分数低的人数,那么他名次最低也就是n-b,得到一个闭区间[a+1,n-b],用map记录下出现的次数,用vector存可行的决策,也就是当这个闭区间出现第一次的时候保留这个区间。

       f[i]=max(f[i],f[j-1]+sum[j][i])sum[j][i]表示[j,i]区间的人数

T3 借教室

       解法一:线段树(COGS极限数据被卡一个点95分,老师的数据卡两个点90分)

裸的操作,读入一个区间先判断区间内的最小值是否小于需要的数量,如果小于就直接输出该操作编号即可,否则区间修改即可,切记查询时的ans要开局部变量(之后更新会覆盖全局变量值),lazy标记只修改单点即可,因为只记录最小值。

       解法二:二分查找

用某一天的前缀和表示该天需要的教室数

比如一开始数列a是0 0 0 0 0 0前缀和0 0 0 0 0 0

3到5需要2的教室

将a[3]+=2,a[6]-=2

数列变为0 0 2 0 0-2

前缀和变为0 0 2 2 20

这样就实现了增加3-5需要的教室数

然后我们二分订单数

处理前mid个订单看看是否有某一天的前缀和大于di 很巧妙,然而COGS上线段树快读可以卡过最后一个点然并卵


CODE 

T1

# include <cstdio>
# include <iostream>
# include <cstring>
# include <queue>

using namespace std;

int T,n,mid,tot,ans;
int hav[30],ned[30];
int dis[30],sum[30],h[30];
bool vis[30];

struct Edge {
	int v,w,next;
} e[10005];

void clr() {
	memset(hav,0,sizeof(hav));
	memset(ned,0,sizeof(ned));
}

void add(int u,int v,int w) {
	e[++tot].v = v;
	e[tot].w = w;
	e[tot].next = h[u];
	h[u] = tot;
}

void build(int lim) {
	tot = 0;
	memset(h,0,sizeof(h));
	for(int i = 1; i <= 24; ++i) {
		add(i - 1,i,hav[i]);
		add(i,i - 1,0);
	}
	for(int i = 1; i < 8; ++i) {
		add(i,i + 24 - 8,lim - ned[i]);
	}
	for(int i = 8; i <= 24; ++i) {
		add(i,i - 8,-ned[i]);
	}
	add(24,0,-lim);
}

bool check(int lim) {
	queue <int> q;
	memset(vis,0,sizeof(vis));
	memset(dis,0x3f,sizeof(dis));
	memset(sum,0,sizeof(sum));
	dis[24] = 0;
	vis[24] = 1;
	sum[24] = 1;
	q.push(24);
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = h[u]; i; i = e[i].next) {
			int v = e[i].v;
			if(dis[v] > dis[u] + e[i].w) {
				dis[v] = dis[u] + e[i].w;
				if(!vis[v]) {
					q.push(v);
					vis[v] = 1;
					if(++sum[v] > 25) {
						return 0;
					}
				}
			}
		}
	}
	if(dis[0] == -lim) return 1;
	return 0;
}

bool divide(int l,int r) {
	ans = -1;
	while(l <= r) {
		mid = (l + r) >> 1;
		build(mid);
		if(check(mid)) ans = mid,r = mid - 1;
		else l = mid + 1;
	}
	if(ans == -1) return 0;
	return 1;
}

void init() {
	int pos;
	for(int i = 1; i <= 24; ++i) scanf("%d",&ned[i]);
	scanf("%d",&n);
	for(int i = 1; i <= n; ++i) {
		scanf("%d",&pos);
		hav[pos + 1]++;
	}
	if(!divide(0,n))
		printf("No Solution\n");
	else printf("%d\n",ans);
}

int main() {
	freopen("ployment.in","r",stdin);
	freopen("ployment.out","w",stdout);
	scanf("%d",&T);
	while(T--) {
		clr();
		init();
	}
}
T2

# define MAXN 100005

# include <cstdio>
# include <cstring>
# include <iostream>
# include <vector>
# include <map>

using namespace std;

int n;
int f[MAXN];
vector <int> c[MAXN];
map < pair<int,int>,int > m;

//dp[i] = max{dp[j-1]+sum[j][i]}

void init() {
	int l,r,a,b;
	scanf("%d",&n);
	for(int i = 1;i <= n;++i) {
		scanf("%d%d",&a,&b);
		l = a + 1;
		r = n - b;
		if(l > r) continue;
		m[make_pair(l,r)]++;
		if(m[make_pair(l,r)] == 1) c[r].push_back(l);
	}
	for(int i = 1;i <= n;++i) {//i youqujian 
		f[i] = f[i - 1];
		for(int j = 0;j < c[i].size();++j) {
			f[i] = max(f[i],(f[c[i][j] - 1] + min(i - c[i][j] + 1,m[make_pair(c[i][j],i)])));
		}
	}
	printf("%d",n - f[n]);
}

int main() {
	freopen("a.in","r",stdin);
	freopen("a.ans","w",stdout);
	init();
}

T3线段树

# define lson l,mid,rt<<1
# define rson mid+1,r,rt<<1|1

# include <cstdio>
# include <cstring>

using namespace std;
const int MAXN = 1500000;

int n,m;
int tree[MAXN];
int add[MAXN];

inline int Min(int a,int b) {
	if(a < b) return a;
	return b;
}

inline int in(){
	char c=getchar();
	int x=0;
	while(c<'0'||c>'9')c=getchar();
	for(;c>='0'&&c<='9';c=getchar())x=(x<<3)+(x<<1)+c-'0';
	return x;
}

void build(int l,int r,int rt) {
	if(l == r) {
		tree[rt] = in();
		return ;
	}
	int mid = (l + r) >> 1;
	int temp = rt << 1;
	build(lson);
	build(rson);
	tree[rt] = Min(tree[temp],tree[temp | 1]);
	return ;
}

inline void clear(int rt) {
	if(add[rt] == 0)
		return ;
	int temp = rt << 1;
	tree[temp] += add[rt];
	tree[temp | 1] += add[rt];
	add[temp] += add[rt];
	add[temp | 1] += add[rt];
	add[rt] = 0;
	tree[rt] = Min(tree[temp],tree[temp | 1]);
	return ;
}

void update(int pos_l,int pos_r,int new_date,int l,int r,int rt) {
	if(pos_l <= l && pos_r >= r) {
		tree[rt] += new_date;
		add[rt] += new_date;
		return ;
	}
	clear(rt);
	int mid = (l + r) >> 1;
	int temp = rt << 1;
	if(pos_l <= mid)
		update(pos_l,pos_r,new_date,lson);
	if(pos_r > mid)
		update(pos_l,pos_r,new_date,rson);
	tree[rt] = Min(tree[temp],tree[temp | 1]);
	return ;
}

int query(int pos_l,int pos_r,int l,int r,int rt) {
	if(pos_l <= l && pos_r >= r)
		return tree[rt];
	clear(rt);	
	int mid = (l + r) >> 1;
	int temp = rt << 1;
	int ans = 0x3fffffff;
	if(pos_l <= mid)
		ans = query(pos_l,pos_r,lson);
	if(pos_r > mid)
		ans = Min(ans,query(pos_l,pos_r,rson));
	return ans;
}

int main() {
	freopen("classroom.in","r",stdin);
	freopen("classroom.out","w",stdout);
	int i,u,v,w;
	n = in();
	m = in();
	build(1,n,1);
	for(i = 1; i <= m; ++i) {
		w = in();
		u = in();
		v = in();
		if(query(u,v,1,n,1) < w) {
			printf("-1\n%d",i);
			return 0;
		}
		update(u,v,-w,1,n,1);
	}
	puts("0");
	return 0;
}

T3 二分

# define MAXN 1000005

# include <cstdio>
# include <cstring>

using namespace std;
typedef long long LL;
int n,m,l,r,mid,ans;
int d[MAXN],hav[MAXN];
int s[MAXN];
int t[MAXN];
LL sum[MAXN];

void init(){
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;++i) scanf("%d",&hav[i]);
	for(int i = 1;i <= m;++i) scanf("%d%d%d",&d[i],&s[i],&t[i]);
}


bool check(int x) {
	memset(sum,0,sizeof(sum));
	for(int i = 1;i <= x;++i) {
		sum[s[i]] += d[i];
		sum[t[i] + 1] -= d[i];
	}
	long long a = 0;
	for(int i = 1;i <= n;++i) {
		sum[i] += sum[i - 1];
		if(sum[i] > hav[i]) return 0;
	}
	return 1;
}

void divide() {
	l = 1,r = m;
	while(l <= r) {
		mid = (l + r) >> 1;
		if(check(mid)) l = mid + 1;
		else r = mid - 1;
	} 	
	if(r == m) {
		printf("0");
	}
	else {
		printf("-1\n%d",l);
	}
	return ;
}

int main() {
	freopen("classroom.in","r",stdin);
	freopen("classroom.out","w",stdout);
	init();
	divide();
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值