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的面积占原多边形的面积比。
半平面交。
首先的限制条件是多边形的边,其次就是面积的限制,这个我们可以用叉积计算。
其实挺简单的,但是太冗杂了,不写了。(其实是不会写….)