fzu_noip 1036(磁盘碎片整理-Dp)

磁盘碎片整理

时限:1s
内存:32M

★问题描述:

Jack最近在PS海报。海报所需各种素材不但让Jack头大,也让硬盘分区中的文件碎片越来越多,电脑的反应速度越来越慢。

烦恼的Jack决定好好整理一下磁盘的碎片。但是,为了体现高手风范,自负的Jack不喜欢系统自带的整理工具,决定自己编写一个,希望你帮他完成简单的第一遍整理。

因为不可移动的系统文件的分隔,Jack的N个文件,分为许多文件块,杂乱分布在M个长度不等的区间中。在第一遍整理时,只将每个区间中的碎片进行顺序改变,不跨区间移动。

因为在Jack完成的第二次整理时,将把M个区间依顺序连接成一个完整区间,希望整理完成后,零碎度越低越好。(若连接后的完整区间,文件块i与文件块i+1属于不同文件,则零碎度+1)。

例:

N=5 M=3

存储区间1:长度3 文件块[4,3,1]

存储区间2:长度4 文件块[2,1,4,4]

存储区间3:长度3 文件块[1,3,1]

第一次整理,第二次连接后:[1,3,4][4,4,2,1][1,1,3],零碎度为5

★数据输入:

第一行n m(1<=n<=10,1<=m<=10000),表示有n个文件,m个存储区间。

接下来m行,每行第一个整数为Li(0<Li<=50),表示存储区间i的长度。接下来Li个整数,分别表示该区间第j文件块所属文件。

★结果输出:

输出两次整理完成后,能达到的最小零碎度。

输入示例

输出示例

5 3

3 4 3 1

4 2 1 4 4

3 1 3 1

5

 

 

简单Dp,f[i][j]表示到第i块硬盘前面接j能取得的最大头尾连接数(开始那个默认与之前的连接),则


F[i][j]=| max(f[i][l[j]],f[i-1][k]+(b[i-1][l[j]]&&(k!=l[j]||pre_size==1))) (i>1)

          |1
(i=1)

答案为tot-max(f[m][k](0<=k<n)) tot为每个区间不同数字类数的总和

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#include<cmath>
using namespace std;
#define MAXN (10)
#define MAXM (10000+10)
#define MAXLi (50)
int n,m;
bool b[MAXM][11];
int f[MAXM][11];
int l[MAXN];
int main()
{
  //  freopen("piece.in","r",stdin);
    memset(b,0,sizeof(b));
    memset(f,0,sizeof(f));
    scanf("%d%d",&n,&m);
    int ans=0,pre_size=0;
    for (int i=1;i<=m;i++)
    {
        int li;
        scanf("%d",&li);
        for (int j=1;j<=li;j++) scanf("%d",&l[j]);
        sort(l+1,l+li+1);
        int size=unique(l+1,l+li+1)-(l+1);
        ans+=size;
    //  cout<<size<<endl;
    //  for (int i=1;i<=size;i++) cout<<l[i]<<' ';cout<<endl;
        for (int j=1;j<=size;j++)
            b[i][l[j]]=1;
        if (i==1)
        {
           for (int j=1;j<=size;j++)
               f[1][l[j]]=1;
        }
        else
        {
        for (int j=1;j<=size;j++)
            for (int k=0;k<n;k++)
                if (b[i-1][k])
                {
                   f[i][l[j]]=max(f[i][l[j]],f[i-1][k]+(b[i-1][l[j]]&&(k!=l[j]||pre_size==1)));
                }
        }
        pre_size=size;
    }
    int p=0;
    for (int j=0;j<n;j++) {p=max(p,f[m][j]); }
/*
    for (int i=1;i<=m;i++)
    {
        for (int j=0;j<n;j++)
            cout<<f[i][j]<<' ';
        cout<<endl;
    }
*/
    cout<<ans-p<<endl;
//   while (1);
    return 0;
}


fzu_noip 1033 (作业问题-拼最大的2,3,5倍数)

作业问题

时限:1s内存:32M

★问题描述:

小T很喜欢数学,每天老师刚布置完作业,他就开始思考,今天他遇到了困难。

现在有很多的数字,你的任务是找出由这些数字组成的最大的数,并且这个数必须能被2,3,5整除。你可以只用其中一部分的数,但不允许出现前导0。

★数据输入:

输入数据的第一行为一个整数N。(1<=N<=1000)表示给出N个数字,每个数字范围是0—9。接下来一行有N个数,数字由空格隔开。

★结果输出:

输出你找出的最大的数,如果不存在这样的数就输出-1。

输入示例

输出示例

1

0

0

11

3 4 5 4 5 3 5 3 4 4 0

5554443330

8

3 2 5 1 5 2 2 3

-1

 

裸裸的贪心。

满足能被2,5整除,当且仅当数中有0且放在末尾

满足能被3整除,要让sum%3=0.

显然要取尽可能多的位数,先判断是否能全取。

用a[i]表示数字i有几个,b[i]表示%3=i的数字有几个.

若全取后%3=1,则删1个%3=1或2个%3=2.优先删删除数字少的,次优先删数字尽量小的。

若无法全取,也不够删,则无解.

同理%3=2.

若有合法解,数字必从大到小排列。

由于不能有前导0,需特判0,0,0,0……的情况。

var
   a:array[0..9] of longint;
   b:array[0..2] of longint;
   n,i,j,p:longint;
procedure decrease(k:longint);
begin
   if (a[k]>0) then dec(a[k])
   else if (a[k+3]>0) then dec(a[k+3])
   else if (a[k+6]>0) then dec(a[k+6]);
end;
begin
   read(n);
   fillchar(a,sizeof(a),0);
   for i:=1 to n do
   begin
      read(j);
      inc(a[j]);  inc(b[j mod 3]);
   end;
   if (a[0]=0) then
   begin
      writeln('-1');
      halt;
   end;

   p:=b[1] mod 3+(b[2]*2) mod 3;
   if (p=1) then
   begin
      if (b[1]>0) then decrease(1)
      else if (b[2]>1) then begin decrease(2);decrease(2); end
      else
      begin
         writeln('-1');
         halt;
      end;
   end;
   if (p=2) then
   begin
      if (b[2]>0) then decrease(2)
      else if (b[1]>1) then begin decrease(1);decrease(1); end
      else
      begin
         writeln('-1');
         halt;
      end;
   end;

   p:=0;
   for i:=1 to 9 do inc(p,a[i]);
   if (p=0) then
   begin
      writeln('0');
      halt;
   end;


   for i:=9 downto 0 do
      for j:=1 to a[i] do write(i);
   writeln;







end.

fzu_noip 1032 (无穷数-进位判定)

无穷数

时限:1s内存:32M

★问题描述:

我们生成两个无穷大的数,第一个数是把所有的自然数链接起来组成的数字;第二个数是把所有自然数的平方连接起来组成的数。对这两个数求和,如下:

 123456789101112131415161718192021...

+ 149162536496481100121144169196225...

= 272619325597593231536305887388246...

现在给你一个整数k,问和从左往右数第k位的数码是多少?

★数据输入:

输入数据有多组,每组数据输入一行,有一个数k。对于100%的数据,k<=2147483647

★结果输出:

对于每组数据,输出一个整数N,从左往右数第k位的数码。

输入示例

输出示例

5

6

7

8

1

9

3

2

 
先算出第k位的Ai和Bi,然后相加,考虑是否加过头。
接下来用search_fi(k:longint) 判断第k位是否会令前一位进位,则


const
   a:array[1..19,1..2] of int64=((1,3),(4,9),(10,31),(32,99),(100,316),(317,999),(1000,3162),(3163,9999),(10000,31622),(31623,99999),(100000,316227),(316228,999999),(1000000,3162277),(3162278,9999999),(10000000,31622776),(31622777,99999999),
   (100000000,316227766),(316227767,999999999),(1000000000,2147483647));
var
   k:longint;
function search_ai(k:longint):longint;
var
   i,j,d,k2,g:int64;
begin
   d:=1;i:=9;
   while (true) do
   begin
      if (k-d*i>0) then
      begin
         dec(k,d*i);
         i:=i*10;inc(d);
      end
      else break;
   end;
   k2:=i div 9+(k-1) div d;
   g:=(k-1) mod d+1;
   g:=d-g+1;
   while (g>1) do begin k2:=k2 div 10;dec(g); end;
   exit(k2 mod 10);
end;
{
function search_bi(k:longint):longint;
var
   i,j:int64;
   head:longint;
begin
   head:=1;j:=10; i:=1;
   while (i<=2147483647 ) do
   begin
      if ((i*i) div j>0) then
      begin
         write('(',head,',',i-1,')',',');
         head:=i; j:=j*10;
      end;
      inc(i);
   end;
end;      }


function search_bi(k:longint):longint;
var
   i:longint;
   g,k2:int64;
begin
   i:=1;
   while (k>i*(a[i,2]-a[i,1]+1)) do
   begin
      dec(k,i*(a[i,2]-a[i,1]+1));
      inc(i);
   end;

   k2:=(k-1) div i+1;
   k2:=a[i,1]-1+k2;
   k2:=k2*k2;
   g:=(k-1) mod i+1;
   g:=i-g+1;

   while (g>1) do begin k2:=k2 div 10;dec(g); end;
   exit(k2 mod 10);
end;
function search_fi(k:longint):longint;
var
   i:longint;
begin
   i:=search_ai(k)+search_bi(k);
   if (i<=8) then exit(0)
   else if (i>9) then exit(1)
   else exit(search_fi(k+1));

end;
begin
//   assign(output,'dabiao.out');
//   rewrite(output);

   while not eof do
   begin
      readln(k);
      writeln((search_bi(k)+search_ai(k)+search_fi(k+1)) mod 10);


   end;

//   search_bi(1);
//close(output);
end.

BZOJ 1088(mine)

1088: [SCOI2005]扫雷Mine

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 659  Solved: 386
[Submit][Status][Discuss]

Description

相信大家都玩过扫雷的游戏。那是在一个n*m的矩阵里面有一些雷,要你根据一些信息找出雷来。万圣节到了,“余”人国流行起了一种简单的扫雷游戏,这个游戏规则和扫雷一样,如果某个格子没有雷,那么它里面的数字表示和它8连通的格子里面雷的数目。现在棋盘是n×2的,第一列里面某些格子是雷,而第二列没有雷,如下图: 由于第一列的雷可能有多种方案满足第二列的数的限制,你的任务即根据第二列的信息确定第一列雷有多少种摆放方案。

Input

第一行为N,第二行有N个数,依次为第二列的格子中的数。(1<= N <= 10000)

Output

一个数,即第一列中雷的摆放方案数。

Sample Input

2

1 1





Sample Output

2

Dp扫雷题:
一开始居然没有想出方程…天生不敏感?

显然方程为f(i,x,x_r) 表示第i格时,中间x和右边x_r是否有雷
轻易得到递归方程:
这题的教训是,方程果断列,另当前状态确定差1,由前面得到。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<functional>
#include<cmath>
using namespace std;
#define MAXN (10000+10)
int n,a[MAXN];
int f[MAXN][2][2]={0}; //Middle and 1
int main()
{
	cin>>n;
	for (int i=1;i<=n;i++) cin>>a[i];
	switch (a[1])
	{
		case 2:f[1][1][1]++;break;
		case 0:f[1][0][0]++;break;
		default:f[1][0][1]=f[1][1][0]=1;
	}
	for (int i=2;i<=n;i++)
	{
		switch (a[i])
		{
			case 3:f[i][1][1]=f[i-1][1][1];break;
			case 0:f[i][0][0]=f[i-1][0][0];break;
			case 2:f[i][1][0]=f[i-1][1][1];f[i][0][1]=f[i-1][1][0];f[i][1][1]=f[i-1][0][1];break;
			case 1:f[i][1][0]=f[i-1][0][1];f[i][0][1]=f[i-1][0][0];f[i][0][0]=f[i-1][1][0];break;
		}
	}
	cout<<f[n][1][0]+f[n][0][0]<<endl;

	return 0;
}

HDU 1465(错排公式)

不容易系列之一

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 9829    Accepted Submission(s): 4115



Problem Description
大家常常感慨,要做好一件事情真的不容易,确实,失败比成功容易多了!
做好“一件”事情尚且不易,若想永远成功而总从不失败,那更是难上加难了,就像花钱总是比挣钱容易的道理一样。
话虽这样说,我还是要告诉大家,要想失败到一定程度也是不容易的。比如,我高中的时候,就有一个神奇的女生,在英语考试的时候,竟然把40个单项选择题全部做错了!大家都学过概率论,应该知道出现这种情况的概率,所以至今我都觉得这是一件神奇的事情。如果套用一句经典的评语,我们可以这样总结:一个人做错一道选择题并不难,难的是全部做错,一个不对。

不幸的是,这种小概率事件又发生了,而且就在我们身边:
事情是这样的——HDU有个网名叫做8006的男性同学,结交网友无数,最近该同学玩起了浪漫,同时给n个网友每人写了一封信,这都没什么,要命的是,他竟然把所有的信都装错了信封!注意了,是全部装错哟!

现在的问题是:请大家帮可怜的8006同学计算一下,一共有多少种可能的错误方式呢?

 


Input
输入数据包含多个多个测试实例,每个测试实例占用一行,每行包含一个正整数n(1<n<=20),n表示8006的网友的人数。
 


Output
对于每行输入请输出可能的错误方式的数量,每个实例的输出占用一行。
 


Sample Input
2 3
 


Sample Output
1 2
 


Author
lcy
 


Source
 


Recommend
lcy
 

难得的中文题。

n错排公式:F[n]=(n-1)*(F[n-1]+F[n-2])

证明:

1.当前n-1个错排时:将其任意一封信与n对调,共(n-1)*F[n-1]

2.当前n-2个错排,1个不错排时,将不错排的那封信与n对调,共(n-1)*F[n-2]

3.当前≤n-3个错排,≥2个不错排时,显然无解.

∴F[n]=(n-1)*F[n-1]+(n-2)*F[n-2]

证毕


记得用long long,不然会爆

#include<cstdio>
#include<cstdlib>
#include<iostream>
using namespace std;
#define MAXN (20+10)
int n;
long long f[MAXN]={0,0,1};
int main()
{
	for (int i=3;i<=20;i++) f[i]=(i-1)*f[i-1]+f[i-2]*(i-1);
	while (cin>>n) cout<<f[n]<<endl;
}

POJ 2954(Pick公式)

Language:
Triangle
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 4203   Accepted: 1858

Description

求出一个已知3点坐标的格点三角形的里面(边上的不算)有多少点.

Input

多组数据。每一行输入x1y1x2y2x3,y3, 表示格点三角形 (x1y1), (x2y2),
(x3y3), −15000 ≤ x1y1x2y2x3y3 ≤ 15000. 数据以6个0结尾.

Output

每组数据输出一行,表示格点三角形里面的点数.

Sample Input

0 0 1 0 0 1
0 0 5 0 0 5
0 0 0 0 0 0

Sample Output

0
6

Source

Pick定理:S=I+E/2-1 (E表示格点多边形边上的点,I表示格点多边形内的点)

Pick推论:格点多边形S=0.5k (k是正整数)


边上格点数公式线段(x1,y1)-(x2,y2)的格点数=gcd(abs(x1-x2),abs(y1-y2))+1


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
#define MAXX (15000)
int n;
int gcd(int a,int b){if (a<b) swap(a,b);if (b==0) return a;else return gcd(b,a%b);}
struct P
{
	int x,y;
	P(){}
	P(int _x,int _y):x(_x),y(_y){}
	friend istream& operator>>(istream& cin,P &a){cin>>a.x>>a.y;return cin;	}
	friend bool operator||(bool b,P &a){return b||a.x||a.y;}
}a[3];
struct S
{
	P s,t;
	S(){}
	S(P _s,P _t):s(_s),t(_t){}
	friend bool operator||(bool b,S &a){return b||a.s||a.t;}
	friend bool operator||(S &a,S &b){return 0||a||b;}
	int dx(){return abs(t.x-s.x);}
	int dy(){return abs(t.y-s.y);}
	int E(){return gcd(dx(),dy())+1;}
};
struct T
{
	S c[3];
	S& operator[](int i){return c[i];}
	friend istream& operator>>(istream& cin,T &c){cin>>a[0]>>a[1]>>a[2];c[0]=S(a[0],a[1]);c[1]=S(a[1],a[2]);c[2]=S(a[2],a[0]);return cin;}
	friend bool operator&&(bool b,T &a){return b&&(a[0]||a[1]||a[2]);}
	int area2(){return c[0].s.x*c[1].s.y+c[1].s.x*c[2].s.y+c[2].s.x*c[0].s.y-c[1].s.x*c[0].s.y-c[2].s.x*c[1].s.y-c[0].s.x*c[2].s.y;}
	int E(){return c[0].E()+c[1].E()+c[2].E()-3;}
	double _S(){return (double)abs(area2())/2;}
	int I(){return _S()-E()/2+1;}
}c;
int main()
{
	while (cin>>c&&c)
	{
		cout<<c.I()<<endl;
	}
	return 0;
}

CF 18A(近似直角三角形判断+向量直角公式+switch+istream&(..&P a))

A. Triangle
time limit per test

2 seconds

memory limit per test

64 megabytes

input

standard input

output

standard output

判断一个格点三角形是直角三角形,近似直角三角形,还是都不是.

Hint:近似直角三角形是指把一个三角形的一个点移动1个单位长度(移动后仍为格点三角形),其能变成直角三角形的非直角三角形

Input

 x1, y1, x2, y2, x3, y3 表示3点坐标(都在格点上),不超过
100.

Output

I直角三角形输出 RIGHT,

近似直角三角形输出
 ALMOST, 都不是输出 NEITHER.

Sample test(s)
input
0 0 2 0 0 1
output
RIGHT
input
2 3 4 5 6 6
output
NEITHER
input
-1 0 2 0 0 1
output
ALMOST

各种判断…

注意格点三角形在移动完可能出现点重合(0向量)

仍满足向量直角公式a X b
= |a||b|

以及switch-case-default的用法。

△:default打错不会提示.

PS:istream& operator<<的重载中 输入的struct 如果不加&,是读不进的。


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<functional>
#include<algorithm>
#include<cmath>
using namespace std;
struct P
{
	int x,y;
	P(){}
	P(int _x,int _y):x(_x),y(_y){}
	friend istream& operator>>(istream& cin,P &a){cin>>a.x>>a.y;return cin;	}
	void move(int d)
	{
		if (d==1) x++;
		if (d==-1) x--;
		if (d==2) y++;
		if (d==-2) y--;
		return;
	}
}a[3];
struct V
{
	int x,y;
	V(){}
	V(int _x,int _y):x(_x),y(_y){}
	V(P a,P b):x(b.x-a.x),y(b.y-a.y){}
	friend int operator*(V a,V b){return a.x*b.y-a.y*b.x;}
	int dis2(){return x*x+y*y;	}
	friend bool right_angle(V a,V b){return pow(a*b,2)==a.dis2()*b.dis2();	}
}c[3];
void res_c()
{
	for (int i=0;i<3;i++) c[i]=V(a[i],a[(i+1)%3]);
}
bool is_r_trangle()
{
	res_c();
	for (int i=0;i<3;i++) if (!c[i].dis2()) return 0;
	for (int i=0;i<2;i++)
		for (int j=i+1;j<=2;j++) if (right_angle(c[i],c[j])) {/*cout<<i<<' '<<j<<endl;*/return 1;}
	return 0;
}
int solve()
{
	if (is_r_trangle()) return 1;
	for (int i=0;i<3;i++)
	{
		for (int j=-2;j<=2;j++)
		{
			if (j==0) continue;
			a[i].move(j);
			if (is_r_trangle()){/*cout<<i<<' '<<j<<endl;*/ return 2;}
			a[i].move(-j);
		}
	}
	return 0;
}
int main()
{
	for (int i=0;i<3;i++) cin>>a[i];

	switch (solve())
	{
		case 1:cout<<"RIGHT";break;
		case 2:cout<<"ALMOST";break;
		default:cout<<"NEITHER";
	}
	cout<<endl;

	return 0;
}

POJ 2398(二分点集)

Language:
Toy Storage
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 8137   Accepted: 3848

Description

在长方形 (x1,y1) (x2,y2) 中有n块板(保证与上下边相交),和m个点。
现给出板和点的位置,求拥有相同点数的区域数、
 
 

Input

多组数据.每组数据开头为 n m x1 y1 x2 y2. n (0 < n <= 5000) m (0 < m <= 5000). (x1,y1)为左上角坐标 , (x2,y2)为右下角坐标. 
接下来 n 行有2个数 Ui Li,表示第i块板为 (Ui,y1) (Li,y2). (保证两两不交).
接下来m 行为点的坐标 Xj Yj (保证不在板上)
数据以 0 结束.

Output

每组数据给出同点数的区域数
点数: 区域数
…(点数1→n,区域数为0不输出)
请按这个格式输出。
每组数据开头输出“Box”。 

Sample Input

5 6 0 10 60 0
3 1
4 3
6 8
10 10
15 30
1 5
2 1
2 8
5 5
40 10
7 9
4 10 0 10 100 0
20 20
40 40
60 60
80 80
 5 10
15 10
25 10
35 10
45 10
55 10
65 10
75 10
85 10
95 10
0

Sample Output

0: 2
1: 1
2: 1
3: 1
4: 0
5: 1

0: 2
1: 2
2: 2
3: 2
4: 2

Hint

落在长方形边上的点也算.

Source

基本同POJ 2318

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
#define MAXN (1000+10) //Board
#define MAXM (1000+10) //Toy
struct P
{
	double x,y;
	P(){}
	P(int _x,int _y):x(_x),y(_y){}
	friend istream& operator>>(istream& cin,P &a){cin>>a.x>>a.y;return cin;	}
}a[MAXM];
struct V
{
	double x,y;
	P s;
	V(){}
	V(P a,P b):x(b.x-a.x),y(b.y-a.y),s(a){}
	friend int operator*(const V a,const V b)
	{
		return a.x*b.y-a.y*b.x;
	}
}c[MAXN];
int n,m,x1,y1,x2,y2,f[MAXM];
int cmp(V a,V b)
{
	return a.s.x<b.s.x;
}
void binary(int L,int R,int l,int r)
{
	if (R-L==1)
	{
		f[r-l+1]++;
		return;
	}
	int i=l,j=r,m=(l+r)>>1;
	V &M=c[(L+R)>>1];
	do
	{
		while (i<=r&&V(M.s,a[i])*M<0) i++;
		while (j>=l&&V(M.s,a[j])*M>0) j--;
		if (i<=j) {swap(a[i],a[j]);i++;j--;	}
	}while (i<=j);

	i--;j++;
	binary(L,(L+R)>>1,l,i);
	binary((L+R)>>1,R,j,r);

}
int main()
{
//	freopen("poj2398.in","r",stdin);

	while (scanf("%d%d",&n,&m)==2)
	{
		cout<<"Boxn";
		memset(f,0,sizeof(f));
		cin>>x1>>y2>>x2>>y1;
		for (int i=1;i<=n;i++)
		{
			int u,l;
			cin>>u>>l;
			c[i]=V(P(l,y1),P(u,y2));
		}
		c[0]=V(P(x1,y1),P(x1,y2));c[n+1]=V(P(x2,y1),P(x2,y2));
		sort(c+1,c+1+n,cmp);
		for (int i=1;i<=m;i++) cin>>a[i];
		binary(0,n+1,1,m);
		for (int i=1;i<=m;i++)
			if (f[i]) cout<<i<<": "<<f[i]<<endl;
	}
	return 0;
}

POJ 2318(点集二分)

Language:
TOYS
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 8137   Accepted: 3848

Description

在长方形 (x1,y1) (x2,y2) 中有n块板(保证与上下边相交),和m个点。
现给出板和点的位置,求各区域点数、
 
 

Input

多组数据.每组数据开头为 n m x1 y1 x2 y2. n (0 < n <= 5000) m (0 < m <= 5000). (x1,y1)为左上角坐标 , (x2,y2)为右下角坐标. 
接下来 n 行有2个数 Ui Li,表示第i块板为 (Ui,y1) (Li,y2). (保证两两不交,且板从左至右给出).
接下来m 行为点的坐标 Xj Yj (保证不在板上)
数据以 0 结束.

Output

每组数据给出各区域点数(最左边区域编号0)
区域编号: 点数
…(区域编号0→n)
请按这个格式输出。
不同组数据间输出一空行。 

Sample Input

5 6 0 10 60 0
3 1
4 3
6 8
10 10
15 30
1 5
2 1
2 8
5 5
40 10
7 9
4 10 0 10 100 0
20 20
40 40
60 60
80 80
 5 10
15 10
25 10
35 10
45 10
55 10
65 10
75 10
85 10
95 10
0

Sample Output

0: 2
1: 1
2: 1
3: 1
4: 0
5: 1

0: 2
1: 2
2: 2
3: 2
4: 2

Hint

落在长方形边上的点也算.

Source

直接枚举点超时,

所以枚举中间那块板,二分查找(注意Qsort性质,[1, i-1]  和 [ j+1,n]即为所求范围)

但是由于中间那块板并不“计入点集”,所以 i 和 j 可能 越界,要特判。

由于用int会乘越界(这题没给范围),所以稳妥的用double.



#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
#define MAXN (5000+10) //Board
#define MAXM (5000+10) //Toy
struct P
{
	double x,y;
	P(){}
	P(int _x,int _y):x(_x),y(_y){}
	friend istream& operator>>(istream& cin,P &a){cin>>a.x>>a.y;return cin;	}
}a[MAXM];
struct V
{
	double x,y;
	P s;
	V(){}
	V(P a,P b):x(b.x-a.x),y(b.y-a.y),s(a){}
	friend int operator*(const V a,const V b)
	{
		return a.x*b.y-a.y*b.x;
	}
}c[MAXN];
int n,m,x1,y1,x2,y2;
void binary(int L,int R,int l,int r)
{
	if (R-L==1)
	{
		cout<<L<<": "<<r-l+1<<endl;
		return;
	}
	int i=l,j=r,m=(l+r)>>1;
	V &M=c[(L+R)>>1];
	do
	{
		while (i<=r&&V(M.s,a[i])*M<0) i++;
		while (j>=l&&V(M.s,a[j])*M>0) j--;
		if (i<=j) {swap(a[i],a[j]);i++;j--;	}
	}while (i<=j);

	i--;j++;
	binary(L,(L+R)>>1,l,i);
	binary((L+R)>>1,R,j,r);

}
int main()
{
//	freopen("poj2318.in","r",stdin);
	scanf("%d%d",&n,&m);
	while (1)
	{
		cin>>x1>>y2>>x2>>y1;
		for (int i=1;i<=n;i++)
		{
			int u,l;
			cin>>u>>l;
			c[i]=V(P(l,y1),P(u,y2));
		}
		c[0]=V(P(x1,y1),P(x1,y2));c[n+1]=V(P(x2,y1),P(x2,y2));
		for (int i=1;i<=m;i++) cin>>a[i];
		binary(0,n+1,1,m);
		if (scanf("%d%d",&n,&m)==2) cout<<endl; else break;
	}
	return 0;
}

POJ 1228(稳定凸包)

Language:
Grandpa's Estate
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 8990   Accepted: 2383

Description

Kamran the Believer继承了祖母的一个凸多边形庄园. 庄园外围用绳子和木桩围起. 但一些绳子和木桩缺失了.帮忙看一下用剩余木桩围起的庄园是否是稳定凸包(即剩下的钉子能确定一个唯一的凸包).

Input

第一行一个数据组数 t (1 <= t <= 10). 
对于每组数据,第一行为 n (1 <= n <= 1000) 表示木桩数. 接下来n行,每行为木桩坐标(x,y),保证整数.

Output

 对每组数据输出YES 或 NO ,表示其是否稳定.

Sample Input

1
6
0 0
1 2
3 4
2 0
2 4
5 0

Sample Output

NO

Source

要想让一个凸包稳定,当且仅当凸包上任意一条边有3个以上的木桩(包括端点

证明:


只要在建完凸包后,枚举,边上的第3点即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXT (10+10)
#define MAXN (1000+10)
//#define sqr( x ) (x*x)
int sqr(int x){return x*x;}
struct P
{
	int x,y;
	P(){}
	P(int _x,int _y):x(_x),y(_y){}
	friend istream& operator>>(istream &cin,P &a)
	{
		cin>>a.x>>a.y;return cin;
	}
	friend double dis(P a,P b)
	{
		return sqrt(double(sqr(a.x-b.x)+sqr(a.y-b.y)));
	}
}a[MAXN];
struct V
{
	int x,y;
	V(){}
	V(int _x,int _y):x(_x),y(_y){}
	V(P a,P b):x(b.x-a.x),y(b.y-a.y){}
	friend int operator*(V a,V b)
	{
		return a.x*b.y-a.y*b.x;
	}
};
int cmp(P A,P B)
{
	int temp=V(a[1],A)*V(a[1],B);
	if (temp>0) return 1;
	else if (temp==0&&dis(a[1],A)<dis(a[1],B)) return 1;
	else return 0;
}
int t,n,st[MAXN];
bool solve()
{
	int size=1;st[0]=1;
	st[1]=1;
	int j=2;
	while (j<=n)
	{
		if (size<2||V(a[st[size-1]],a[st[size]])*V(a[st[size]],a[j])>0)
		{
			st[++size]=j++;
		}
		else size--;
	}
	a[++n]=a[1];
	st[++size]=n;

	for (int i=1;i<size;i++)
	{
	/*
		int k=st[i-1]+1;
		for (;k<st[i+1];k++)
			if (k!=st[i]&&V(a[st[i]],a[st[i+1]])*V(a[st[i]],a[st[k]])==0) break;
		if (k==st[i+1]) return 0;
	*/
		int k=1;
		for (;k<n;k++)
			if (k!=st[i]&&k!=st[i+1]&&V(a[st[i]],a[st[i+1]])*V(a[st[i]],a[k])==0) break;
		if (k==n) return 0;
	}
	return size>=4;
}
int main()
{
//	freopen("poj1228.in","r",stdin);
	cin>>t;
	while (t--)
	{
		cin>>n;
		for (int i=1;i<=n;i++) cin>>a[i];
		if (n<6)
		{
			cout<<"NOn";
			continue;
		}
		int p=1;
		for (int i=2;i<=n;i++) if (a[i].x<a[p].x||(a[i].x==a[p].x)&&(a[i].y<a[p].y)) p=i;
		swap(a[1],a[p]);
		sort(a+2,a+1+n,cmp);
//		cout<<dis(P(0,0),P(1,0))<<dis(P(0,0),P(1,0));
		if (solve()) cout<<"YESn";
		else cout<<"NOn";
	}
	return 0;
}

备注:不知为何,将sqr替换成define 结果会输出"nan"……