SCOI2015 day1

本文解析了SC省选day1(scoi2015)的三道题目,包括矩阵元素选择问题、环状区间覆盖问题及凸多边形点集面积比问题,提供了详细的算法思路与代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SC省选day1(scoi2015)

       T1:大意:给出一个n*m的矩阵,从中选出n个数(m>=n),保证每一行每一列都只有一个数被选中,使得选出的数中第k大的最小。(n,m<=250)

       我们先把问题转化成求第n-k+1小的数最小,那么我们先二分答案,然后对于第i行,第j列的数,如果它小于当前val,从i到j连一条边,否则不管,如果最后的最大流大于等于n-k+1,就说明当前方案可行,否则不行。

 

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
struct node{int to;int next;int len;
};node bian[1000010];
int size = 1,first[1010],s,t,n,m,A[260][260],dis[1010],head,tail;
int p[100010],k,maxx = -1;
bool exist[1010];
void inser(int x,int y,int z) {
	bian[ ++ size].to = y;
	bian[size].next = first[x];
	first[x] = size;
	bian[size].len = z;
}
void build_edge(int x) {
	size = 1;
	memset(bian,0,sizeof(bian));
	memset(first,0,sizeof(first));
	s = 0,t = n + m + 1;
	for(int i = 1;i <= n;i ++)
	{
		inser(s,i,1);
		inser(i,s,0);
	}
	for(int i = 1;i <= m;i ++)
	{
		inser(n + i,t,1);
		inser(t,n + i,0);
	}
	for(int i = 1;i <= n;i ++)
		for(int j = 1;j <= m;j ++)
			if(A[i][j] <= x)
			{
				inser(i,n + j,1);
				inser(n + j,i,0);
			}
}
bool bfs(int x,int y) {
	memset(dis,127,sizeof(dis));
	memset(exist,false,sizeof(exist));
	dis[x] = 0;
	head = 0,tail = 1;
	p[1] = x;
	while(head != tail) 
	{
		int k = p[ ++ head];
		exist[k] = false;
		for(int u = first[k];u;u = bian[u].next)
			if(dis[bian[u].to] > dis[k] + 1 && bian[u].len > 0)
			{
				dis[bian[u].to] = dis[k] + 1;
				if(!exist[bian[u].to])
				{
					p[ ++ tail] = bian[u].to;
					exist[bian[u].to] = true;
				}
			}
	}
	if(dis[y] <= 1000000) return true;
	return false;
}
int dfs(int x,int flow) {
	if(x == t) return flow;
	int ret = 0;
	for(int u = first[x];u && flow > 0;u = bian[u].next)
		if(dis[bian[u].to] == dis[x] + 1 && bian[u].len > 0)
		{
			int D = dfs(bian[u].to,min(flow,bian[u].len));
			ret += D;
			flow -= D;
			bian[u].len -= D;
			bian[u ^ 1].len += D;
		}
	if(ret == 0 || flow == 0) dis[x] = -5;
	return ret;
}
int maxflow() {
	int ret = 0;
	while(bfs(s,t))
		ret += dfs(s,1000000000);
	return ret;
}
bool check(int x) {
	build_edge(x);
	int Match = maxflow();
	if(Match >= n - k + 1) return true;
	return false;
}
int main() {
	scanf("%d%d%d",&n,&m,&k);
	for(int i = 1;i <= n;i ++)
	{
		for(int j = 1;j <= m;j ++)
		{
			scanf("%d",&A[i][j]);
			maxx = max(maxx,A[i][j]);
		}
	}
	int l = 1,r = maxx;
	while(l != r)
	{
		int Mid = (l + r) >> 1;
		if(check(Mid)) r = Mid;
		else l = Mid + 1;
	}
	printf("%d\n",l);
	return 0;
}

       T2:大意:给出一个环与n个区间,要求求出对于所有的区间i,在选定了i区间之后最少再选择几个区间使得整个环被覆盖。

       我们先把区间排序后倍长(如果当前区间r<l(因为是环),我们就把r加上区间的长度来减少特判的情况)。

       我们这样考虑,选择了i,那么下一个选择的一定是能够和i相接,并且r尽量靠右的一个区间,这个我们可以用单调队列实现(据说可以二分…)。

       然后就可以倍增啦,设f[i][j]为选了i区间之后一共选了2^j个区间,下一个该选的区间是什么,A[i][j]设为选了之后的右区间最远是哪儿,对于每一个区间求出答案就可以了。

 

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
struct node{int l;int r;int Ans;int id;
};node line[600010];
int n,m,f[600010][21],A[600010][21],next,head,tail,g[600010];
bool comp(const node &x,const node &y) {
	if(x.l != y.l) return x.l < y.l;
	return x.r < y.r;
}
bool back(const node &x,const node &y) {return x.id < y.id;}
int main() {
	scanf("%d%d",&n,&m);
	if(n == 1) printf("1"),exit(0);
	for(int i = 1;i <= n;i ++) 
	{
		line[i].id = i;
		scanf("%d",&line[i].l);
		scanf("%d",&line[i].r);
		if(line[i].r <= line[i].l)
			line[i].r += m;	
	}
	sort(line + 1,line + n + 1,comp);
	for(int i = 1;i <= n;i ++)
	{
		line[n + i].id = i + n;
		line[n + i].l = line[i].l + m;
		line[n + i].r = line[i].r + m;
	}
	next = 2;head = 1,tail = 0;
	for(int i = 1;i <= 2 * n;i ++)
	{
		while(head <= tail && g[head] <= i) head ++;
		while(next <= 2 * n && line[next].l <= line[i].r)
		{
			while(head <= tail && line[g[tail]].r <= line[next].r)
				tail --;
			g[ ++ tail] = next;next ++;
		} 
		if(head <= tail) f[i][0] = g[head],A[i][0] = line[i].r;
	}	
	for(int i = 1;i <= 20;i ++)
	{
		for(int j = 1;j <= 2 * n;j ++)
		{
			A[j][i] = A[f[j][i - 1]][i - 1];
			f[j][i] = f[f[j][i - 1]][i - 1];
		}
	}
	for(int i = 1;i <= n;i ++)
	{
		int Now = i,ret = 0;
		for(int j = 20;j >= 0;j --)
			if(f[Now][j] != 0 && A[Now][j] - line[i].l < m)
				Now = f[Now][j],ret += (1 << j);
		line[i].Ans = ret + 1;
	}
	sort(line + 1,line + n + 1,back);
	for(int i = 1;i <= n - 1;i ++) printf("%d ",line[i].Ans);
	printf("%d",line[n].Ans);
	return 0;
}

       T3:大意:给出一个凸多边形,设点集s为满足使得点0,1,p(p为当前点集中的点)是所有的i,i+1,p中最小的三角形的点的集合,求点集s的面积占原多边形的面积比。

       半平面交。

       首先的限制条件是多边形的边,其次就是面积的限制,这个我们可以用叉积计算。

       其实挺简单的,但是太冗杂了,不写了。(其实是不会写….)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值