上学路线(递归sap-不完全)

Problem 2 上学路线(route.cpp/c/pas)

【题目描述】

可可和卡卡家住马赛克市的东郊,每天上学他们都要转车多次才能到达市区西端的学校。直到有一天他们两人参加了学校的信息学奥林匹克竞赛小组才发现每天上学的乘车路线不一定是最优的。

马赛克市一共设有N个公交车站,不妨将它们编号为1…N的自然数,并认为可可和卡卡家住在1号汽车站附近,而他们学校在N号汽车站。市内有M条直达汽车路线,执行第i条路线的公交车往返于站点pi和qi之间,从起点到终点需要花费的时间为ti。(1<=i<=M, 1<=pi, qi<=N)

两个人坐在电脑前,根据上面的信息很快就编程算出了最优的乘车方案。然而可可忽然有了一个鬼点子,他想趁卡卡不备,在卡卡的输入数据中删去一些路线,从而让卡卡的程序得出的答案大于实际的最短时间。而对于每一条路线i事实上都有一个代价ci:删去路线的ci越大卡卡就越容易发现这个玩笑,可可想知道什么样的删除方案可以达到他的目的而让被删除的公交车路线ci之和最小。

马赛克市的公交网络十分发达,你可以认为任意两个车站间都可以通过直达或转车互相到达,当然如果在你提供的删除方案中,家和学校无法互相到达,那么则认为上学需要的最短为正无穷大:这显然是一个合法的方案。

 

【输入格式】

输入文件中第一行有两个正整数N和M,分别表示马赛克市公交车站和公交汽车路线的个数。以下M行,每行(第i行,总第(i+1)行)用四个正整数(之间由一个空格隔开)描述第i条路线:pi, qi, ti, ci;具体含义见上文描述。

【输出格式】

输出文件最多有两行。 第一行中仅有一个整数,表示从可可和卡卡家到学校需要的最短时间。第二行输出一个整数C,表示Ci之和

【样例输入】

6 7

1 21 3

2 61 5

1 31 1

3 41 1

4 61 1

5 61 2

1 51 4

【样例输出】

2

5

【数据范围】

   

2<=N<=500,1<=M<=124 750, 1<=ti, ci<=10 000

 

这题我用的是递归sap(),谁知道狂Wa。

大家用递归sap的时候想必经常会莫名NTR。

结论://dfs_sap()的Z正确性目前有待考证。。。

根据我的研究,经验总结如下:

1.退出条件:

-PS:有可能某次增广flow=0,但是改距离标号可以继续flow

-PS:有时候无限增广,输答案能看出上面的错误

2.修改距离标号:

-PS:距离标号最好直接+1,minj等手段极有可能造成≈没有距离标号优化的状况

3.dfs时的flow

-PS:一定要提前return,要不然改距离标号a~

4.重构图:

-PS:这样是质的差别,递归不卡时间?

5.唯一重点:

PS:实在不行时,去掉距离标号优化!另可T到死,不能WA到爆

6.用递归sap唯一的好处是省事……(什么你1分钟BFS_sap()?当我没说)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define MAXN (500+10)
#define MAXM (124750+10)
#define MAXCi (10000+10)
#define INF (2139062143)
#define For(i,n) for(int i=1;i<=n;i++)
#define Forp(p,u) for (int p=pre[u];p;p=next[p])
using namespace std;
int n,m,edge[2*MAXM],weight[2*MAXM],c[2*MAXM],next[2*MAXM],pre[MAXN],size=1;
void addedge(int u,int v,int w,int w2)
{
	edge[++size]=v;
	weight[size]=w;
	c[size]=w2;
	next[size]=pre[u];
	pre[u]=size;
}
int d[MAXN],q[MAXN*8];
void spfa()
{
	memset(d,127,sizeof(d));
	d[1]=0;
	int head=1,tail=1;q[1]=1;
	while (head<=tail)
	{
		int &u=q[head];
		Forp(p,u)
		{
			int &v=edge[p];
			if (d[v]==INF||d[u]+weight[p]<d[v])
			{
				q[++tail]=v;d[v]=d[u]+weight[p];
			}
		}
		head++;
	}
}
int d2[MAXN],cnt[MAXN];
void spfa2()
{
	memset(d2,127,sizeof(d2));
	memset(cnt,0,sizeof(cnt));
	d2[n]=0;cnt[0]=1;
	int head=1,tail=1;q[1]=n;
	while (head<=tail)
	{
		int &u=q[head];
		Forp(p,u)
		{
			int &v=edge[p];
			if (d[u]-weight[p]!=d[v]) continue;
			if (d2[v]==INF)
			{
				q[++tail]=v;d2[v]=d2[u]+1;
				cnt[d2[v]]++;
			}
		}
		head++;
	}
}
bool sap_T=1,b[2*MAXM]={0};
int sap(int u,int flow)
{
	if (u==n) return flow;
	int tot=0,minj=INF;
	Forp(p,u)
	{
		int &v=edge[p];
	/*
		if (d2[v]==INF) continue;
		if (!(d[u]+weight[p]==d[v]||d[u]-weight[p]==d[v])) continue;
	*/
		if (!b[p]) continue;
		if (c[p]>0&&d2[u]==d2[v]+1)
		{
			int w=sap(v,min(flow-tot,c[p]));
			c[p]-=w;c[p^1]+=w;tot+=w;
			if (flow==tot) return tot;
		}else if (c[p]) minj=min(minj,d2[v]);
	}
	if (--cnt[d2[u]]==0)
		{
			d2[1]=n,sap_T=0;/*return tot;*/
		}//d2[1]=n+1;
//	if (minj^INF) cnt[d2[u]=minj+1]++;
/*	else*/ cnt[++d2[u]]++;
	return tot;
}
int main()
{
	freopen("route.in","r",stdin);
	freopen("route.out","w",stdout);
	memset(pre,0,sizeof(pre));
	scanf("%d%d",&n,&m);
	For(i,m)
	{
		int u,v,w1,w2;
		scanf("%d%d%d%d",&u,&v,&w1,&w2);
		addedge(u,v,w1,w2);
		addedge(v,u,w1,w2);
	}
	spfa();
//	for (int i=1;i<=n;i++) cout<<d[i]<<' ';cout<<endl;
	cout<<d[n]<<endl;
	int ans=0;
	spfa2();
	int de_bug_1=0;
	For(i,n)
		Forp(p,i)
		{
			if (d[i]>d[edge[p]]) c[p]=0;
		//*0
		//	if (d2[i]!=INF&&d2[edge[p]]!=INF&&c[p]) cout<<i<<' '<<edge[p]<<endl,de_bug_1++;
			if (d[i]+weight[p]==d[edge[p]]) b[p]=b[p^1]=1,/*cout<<i<<' '<<edge[p]<<endl,*/de_bug_1++;
		}
//	cout<<de_bug_1<<endl;
	b[0]=1;
	For(i,n)
		Forp(p,i)
		{
			while (next[p]&&!b[next[p]]) next[p]=next[next[p]];
		}

	while(/*sap_T*/1)
	{
		if (d2[1]>=n) break;
		int w=sap(1,INF);
	//	if (!w) break;
//		cout<<ans<<' '<<d2[1]<<endl;
		ans+=w;
	}
	cout<<ans<<endl;
	return 0;
}

BZOJ 3097(Hash Killer I-哈希%u64的不可行)

3097: Hash Killer I

Time Limit: 5 Sec  Memory Limit: 128 MBSec  Special Judge
Submit: 76  Solved: 34
[Submit][Status][Discuss]

Description

这天天气不错,hzhwcmhf神犇给VFleaKing出了一道题:
给你一个长度为N的字符串S,求有多少个不同的长度为L的子串。
子串的定义是S[l]、S[l + 1]、... S[r]这样连续的一段。
两个字符串被认为是不同的当且仅当某个位置上的字符不同。

VFleaKing一看觉得这不是Hash的裸题么!于是果断写了哈希 + 排序。
而hzhwcmhf神犇心里自然知道,这题就是后缀数组的height中 < L的个数 + 1,就是后缀自动机上代表的长度区间包含L的结点个数,就是后缀树深度为L的结点的数量。
但是hzhwcmhf神犇看了看VFleaKing的做法表示非常汗。于是想卡掉他。

VFleaKing使用的是字典序哈希,其代码大致如下:
u64 val = 0;
for (int i = 0; i < l; i++)
 val = val * base + s[i] - 'a';
u64是无符号int64,范围是[0, 2^64)。VFleaKing让val自然溢出。
base是一个常量,VFleaKing会根据心情决定其值。
VFleaKing还求出来了base ^ l,即base的l次方,这样就能方便地求出所有长度为L的子串的哈希值。
然后VFleaKing给哈希值排序,去重,求出有多少个不同的哈希值,把这个数作为结果。
其算法的C++代码如下:

typedef unsigned long long u64;

const int MaxN = 100000;

inline int hash_handle(const char *s, const int &n, const int &l, const int &base)
{
 u64 hash_pow_l = 1;
 for (int i = 1; i <= l; i++)
  hash_pow_l *= base;

 int li_n = 0;
 static u64 li[MaxN];

 u64 val = 0;
 for (int i = 0; i < l; i++)
  val = val * base + s[i] - 'a';
 li[li_n++] = val;
 for (int i = l; i < n; i++)
 {
  val = val * base + s[i] - 'a';
  val -= (s[i - l] - 'a') * hash_pow_l;
  li[li_n++] = val;
 }

 sort(li, li + li_n);
 li_n = unique(li, li + li_n) - li;
 return li_n;
}

hzhwcmhf当然知道怎么卡啦!但是他想考考你。

Input

没有输入。

Output

你需要输出一组数据使得VFleaKing的代码WA掉。我们会使用Special Judge检查你的结果的正确性。
输出文件共两行。
第一行两个用空格隔开的数n、l。
第二行是一个长度为n的字符串。只能包含'a'~'z'。
需要保证1 <= n <= 10^5, 1 <= l <= n,
不符合以上格式会WA。
不要有多余字符,很可能导致你WA。

Sample Input

没有

Sample Output

8 4

buaabuaa

(当然这个输出是会WA的)



HINT

orz 波兰人 & fotile96 & sillycross

这题要一定的数论知识(其实不要……)

如果s[1]=1 s[i]=s[i-1]+s[i-1].flip(1...size)

则有

strlen(s[i])=2^(i-1)

若设f[i]=hash(s[i])-(hash(!s[i]))=2^k * t (t是奇数)

则易证(把hash值有base带,慢慢算……)

f[i]=f[i-1]*(base^[2^(i-2)]-1)

∵2^i-1=(2^(i-1)-1)*(2^(i-1)+1),

所以k很大。  

如果base是偶数,那么只要最高位不同,其它都相同让其溢出就行。


#include<cstdio>
#include<iostream>
#include<functional>
#include<bitset>
#include<string>
using namespace std;
#define MAXN (100000+10)
int n,l;
bitset<MAXN> s;
int main()
{
	n=MAXN-10;
	int bin=1;
	s[1]=1;
	for (int i=2;i<=13;i++,bin<<=1)
	{
		for(int j=bin+1;j<=bin*2;j++)
			s[j]=s[j-bin]^1;
	}
	cout<<n<<' '<<bin/2<<endl;
	for (int i=1;i<=bin;i++)
	{
		if (s[i]) cout<<'a';
		else cout<<'b';
	}
	cout<<'b';
	for (int i=bin+2;i<=n;i++) cout<<'a';

	cout<<endl;
	return 0;
}

PE 47(Distinct primes factors-Python数组)

Distinct primes factors

Problem 47

The first two consecutive numbers to have two distinct prime factors are:

14 = 2 × 7
15 = 3 × 5

The first three consecutive numbers to have three distinct prime factors are:

644 = 2² × 7 × 23
645 = 3 × 5 × 43
646 = 2 × 17 × 19.

Find the first four consecutive integers to have four distinct prime factors. What is the first of these numbers?

Answer:
134043
Completed on Sat, 6 Apr 2013, 05:40

Go to the thread for problem 47 in the forum.

本题考察Python中数组的运用:
n=1000000
a=[0 for i in range(n+1)]
i=2
while i<=n:
    if (a[i]==0):
        j=2*i
        while j<n:
            a[j]=a[j]+1
            j+=i
    i+=1
for i in range(1,n+1-3):
    if (a[i]==4 and a[i+1]==4 and a[i+2]==4 and a[i+3]==4):
        print i

UVA 11538(Chess Queen-矩阵对角线长度)

Problem A
Chess Queen
Input:
Standard Input

Output: Standard Output

 

王后互相攻击的前提是在一行,一列,或一对角线。:

 

在 (NxM) 的棋盘上 2 王后互相攻击,求方案数.

 

Input

 

输入数据不超过 5000 行 ,每行为M and N (0< M, N£106) ,数据以0 0结尾.

 

Output

 

每行一个整数表示方案数,保证它在u64范围内.

 

Sample Input                              Output for Sample Input

2 2

100 223

2300 1000

0 0

12

10907100

11514134000                                                                        

 

Problemsetter: Shahriar Manzoor

Special Thanks to: Mohammad Mahmudur Rahman

首先,一个矩形的长宽若为m,n(m>=n)

那么它一个方向的对角线应为1..(n-1)各2条,n有(m-n+1)条

知道这个的化,本题就转化为,在一列一行或一对角线任取2点,有几种取法。

#include<cstdio>
#include<algorithm>
#include<functional>
#include<iostream>
#include<cstdlib>
#include<cstring>
using namespace std;
#define MAXN (1000000+10)
unsigned long long n,m;
int main()
{
	while (cin>>n>>m&&n&&m)
	{
		if (n>m) swap(n,m);
		cout<<n*m*(n+m-2)+2*n*(n-1)*(3*m-n-1)/3<<endl;
	}
	return 0;
}



BZOJ 3098(Hash Killer II-生日攻击)

3098: Hash Killer II

Time Limit: 5 Sec  Memory Limit: 128 MBSec  Special Judge
Submit: 66  Solved: 22
[Submit][Status][Discuss]

Description

这天天气不错,hzhwcmhf神犇给VFleaKing出了一道题:
给你一个长度为N的字符串S,求有多少个不同的长度为L的子串。
子串的定义是S[l]、S[l + 1]、... S[r]这样连续的一段。
两个字符串被认为是不同的当且仅当某个位置上的字符不同。

VFleaKing一看觉得这不是Hash的裸题么!于是果断写了哈希 + 排序。
而hzhwcmhf神犇心里自然知道,这题就是后缀数组的height中 < L的个数 + 1,就是后缀自动机上代表的长度区间包含L的结点个数,就是后缀树深度为L的结点的数量。
但是hzhwcmhf神犇看了看VFleaKing的做法表示非常汗。于是想卡掉他。

VFleaKing使用的是字典序哈希,其代码大致如下:
u64 val = 0;
for (int i = 0; i < l; i++)
 val = (val * base + s[i] - 'a') % Mod;
u64是无符号int64,范围是[0, 2^64)。
base是一个常量,VFleaKing会根据心情决定其值。
Mod等于1000000007。
VFleaKing还求出来了base ^ l % Mod,即base的l次方除以Mod的余数,这样就能方便地求出所有长度为L的子串的哈希值。
然后VFleaKing给哈希值排序,去重,求出有多少个不同的哈希值,把这个数作为结果。
其算法的C++代码如下:

typedef unsigned long long u64;

const int MaxN = 100000;

inline int hash_handle(const char *s, const int &n, const int &l, const int &base)
{
 const int Mod = 1000000007;

 u64 hash_pow_l = 1;
 for (int i = 1; i <= l; i++)
  hash_pow_l = (hash_pow_l * base) % Mod;

 int li_n = 0;
 static int li[MaxN];

 u64 val = 0;
 for (int i = 0; i < l; i++)
  val = (val * base + s[i] - 'a') % Mod;
 li[li_n++] = val;
 for (int i = l; i < n; i++)
 {
  val = (val * base + s[i] - 'a') % Mod;
  val = (val + Mod - ((s[i - l] - 'a') * hash_pow_l) % Mod) % Mod;
  li[li_n++] = val;
 }

 sort(li, li + li_n);
 li_n = unique(li, li + li_n) - li;
 return li_n;
}

hzhwcmhf当然知道怎么卡啦!但是他想考考你。

Input

没有输入。

Output

你需要输出一组数据使得VFleaKing的代码WA掉。我们会使用Special Judge检查你的结果的正确性。
第一行两个用空格隔开的数n、l。
第二行是一个长度为n的字符串。只能包含'a'~'z'。
需要保证1 <= n <= 10^5, 1 <= l <= n,
不符合以上格式会WA。
不要有多余字符,很可能导致你WA。

Sample Input

没有

Sample Output

8 4

buaabuaa

(当然这个输出是会WA的)

HINT

如果一个房间里有23个或23个以上的人,那么至少有两个人的生日相同的概率要大于50%。

Source

生日攻击:如果你在n个数中随机选数,那么最多选√n次就能选到相同的数(不考虑Rp broken)

同样的,这题的Hash值在0到1000000007.

那就要选差不多10^5次

唯一注意的是l要取大,使得方案数超过Mod

否则就不可能有2个数有相同的Hash值

#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cstring>
#include<iostream>
using namespace std;
#define MAXN (100000)
int n=MAXN,l=13;
int main()
{
	cout<<n<<' '<<l<<endl;
	for(int i=1;i<=n;i++) cout<<char(rand()%26+'a');
	cout<<endl;
	return 0;
}




POJ 2484(博弈-对称博弈)

A Funny Game
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 3345   Accepted: 1960

Description

Alice and Bob decide to play a funny game. At the beginning of the game they pick n(1 <= n <= 106) coins in a circle, as Figure 1 shows. A move consists in removing one or two adjacent coins, leaving all other coins untouched. At least one coin must
be removed. Players alternate moves with Alice starting. The player that removes the last coin wins. (The last player to move wins. If you can't move, you lose.) 
 

Note: For n > 3, we use c1, c2, ..., cn to denote the coins clockwise and if Alice remove c2, then c1 and c3 are NOT adjacent! (Because there is an empty place between c1 and c3.) 

Suppose that both Alice and Bob do their best in the game. 
You are to write a program to determine who will finally win the game.

Input

There are several test cases. Each test case has only one line, which contains a positive integer n (1 <= n <= 106). There are no blank lines between cases. A line with a single 0 terminates the input. 

Output

For each test case, if Alice win the game,output "Alice", otherwise output "Bob". 

Sample Input

1
2
3
0

Sample Output

Alice
Alice
Bob

Source

POJ Contest,Author:Mathematica@ZSU

博弈第一招——对称博弈。

思路:无论对方怎么取,我都取与它对称(或者相反/一样的)

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cstring>
#include<iostream>
using namespace std;
int main()
{
	int n;
	while(scanf("%d",&n)&&n)
	{
		if (n>=3) cout<<"Bob"<<endl;
		else cout<<"Alice"<<endl;
	}
	return 0;
}

POJ 2362(Square-搜索剪枝1-相对顺序)

Language:
Square
Time Limit: 3000MS   Memory Limit: 65536K
Total Submissions: 17066   Accepted: 5878

Description

Given a set of sticks of various lengths, is it possible to join them end-to-end to form a square?

Input

The first line of input contains N, the number of test cases. Each test case begins with an integer 4 <= M <= 20, the number of sticks. M integers follow; each gives the length of a stick - an integer between 1 and 10,000.

Output

For each case, output a line containing "yes" if is is possible to form a square; otherwise output "no".

Sample Input

3
4 1 1 1 1
5 10 20 30 40 50
8 1 7 2 6 4 4 3 5

Sample Output

yes
no
yes

Source

《搜索是怎样剪枝的-1》

1.只要找到3条边。

2.从大到小顺序找。

3.每次搜边时要确定边的相对顺序唯一(直接从TLE→秒)


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cstring>
#include<iostream>
using namespace std;
#define MAXM (20+10)
int tt,n,a[MAXM],cnt,len;
bool b[MAXM],flag;
bool dfs(int l,int x,int pre)
{
//	cout<<l<<' '<<x<<' '<<kth<<endl;
	if (x==len) {l++;x=0;pre=n-1;}
	if (l==4)
	{
		return 1;
	}
	for(int i=pre-1;i;i--)
		if (!b[i]&&x+a[i]<=len)
		{
			b[i]=1;
			if (dfs(l,x+a[i],i)) return 1;
			b[i]=0;
		//	if (!x) return 0;
		}
	return 0;
}
int main()
{
	scanf("%d",&tt);
	while (tt--)
	{
		cnt=0;
		scanf("%d",&n);
		for (int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			cnt+=a[i];b[i]=0;
		}
		sort(a+1,a+1+n);
		if (n<4||cnt%4||a[n]>cnt/4)
		{
			printf("non");continue;
		}
		b[n]=1;len=cnt/4;
		if (dfs(1,a[n],n))
		{
			printf("yesn");
		}
		else printf("non");
	}
	return 0;
}


POJ 2553(The Bottom of a Graph-缩点求出度)

Language:
The Bottom of a Graph
Time Limit: 3000MS   Memory Limit: 65536K
Total Submissions: 7308   Accepted: 3003

Description

We will use the following (standard) definitions from graph theory. Let V be a nonempty and finite set, its elements being called vertices (or nodes). Let E be a subset of the Cartesian product V×V, its elements being called edges.
Then G=(V,E) is called a directed graph. 
Let n be a positive integer, and let p=(e1,...,en) be a sequence of length n of edges ei∈E such that ei=(vi,vi+1) for a sequence of vertices (v1,...,vn+1).
Then p is called a path from vertex v1 to vertex vn+1 in G and we say that vn+1 is reachable from v1, writing (v1→vn+1)
Here are some new definitions. A node v in a graph G=(V,E) is called a sink, if for every node w in G that is reachable from vv is also reachable from w. The bottom of a graph is the subset of
all nodes that are sinks, i.e.,bottom(G)={v∈V|∀w∈V:(v→w)⇒(w→v)}. You have to calculate the bottom of certain graphs.

Input

The input contains several test cases, each of which corresponds to a directed graph G. Each test case starts with an integer number v, denoting the number of vertices of G=(V,E), where the vertices will be identified by the integer
numbers in the set V={1,...,v}. You may assume that 1<=v<=5000. That is followed by a non-negative integer e and, thereafter, e pairs of vertex identifiers v1,w1,...,ve,we with
the meaning that (vi,wi)∈E. There are no edges other than specified by these pairs. The last test case is followed by a zero.

Output

For each test case output the bottom of the specified graph on a single line. To this end, print the numbers of all nodes that are sinks in sorted order separated by a single space character. If the bottom is empty, print an empty line.

Sample Input

3 3
1 3 2 3 3 1
2 1
1 2
0

Sample Output

1 3
2

Source

Tarjen缩点统计出度。


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cstring>
#include<iostream>
using namespace std;
#define MAXN (5000+10)
#define MAXM (1000000+10)
int n,m,h[MAXN],t[MAXN],dfs[MAXN],tim,kind,s[MAXN],tot;
bool b[MAXN];
int edge[MAXM],next[MAXM],pre[MAXN],size;
int x[MAXM],y[MAXM],numk[MAXN];
void addedge(int u,int v)
{
	edge[++size]=v;
	next[size]=pre[u];
	pre[u]=size;
}
void tar(int u)
{
	dfs[u]=t[u]=++tim;b[u]=1;s[++tot]=u;
	for (int p=pre[u];p;p=next[p])
	{
		int &v=edge[p];
		if (!b[v]) tar(v),dfs[u]=min(dfs[u],dfs[v]);
		else if (!h[v]) dfs[u]=min(dfs[u],t[v]);  //保证指向祖先
	}
	if (dfs[u]==t[u])
	{
		kind++;
		while (s[tot]!=u) h[s[tot--]]=kind,numk[kind]++;
		h[s[tot--]]=kind;numk[kind]++;

	}
}
int outdegree[MAXN];
int main()
{
	while (scanf("%d",&n)&&n)
	{
		scanf("%d",&m);
		tot=size=tim=kind=0;
		memset(h,0,sizeof(h));
		memset(t,0,sizeof(t));
		memset(dfs,0,sizeof(dfs));
		memset(s,0,sizeof(s));
		memset(pre,0,sizeof(pre));
		memset(b,0,sizeof(b));
		memset(numk,0,sizeof(numk));
		memset(outdegree,0,sizeof(outdegree));

		for (int i=1;i<=m;i++)
		{
			scanf("%d%d",&x[i],&y[i]);
			addedge(x[i],y[i]);
		}
		for (int i=1;i<=n;i++) if (!b[i]) tar(i);
//		for (int i=1;i<=n;i++) cout<<h[i]<<' ';cout<<endl;
		for (int i=1;i<=m;i++)
		{
			if (h[x[i]]^h[y[i]]) outdegree[h[x[i]]]++;
		}
		/*
		int ans=0;
		for (int i=1;i<=kind;i++)
			if (!outdegree[i]) ans+=numk[i];
		cout<<ans<<endl;
		*/
		bool flag=0;
		for (int i=1;i<=n;i++)
			if (!outdegree[h[i]])
			{
				if (flag) printf(" ");else flag=1;printf("%d",i);
			}


	//	cout<<ans<<endl;
		cout<<endl;
	}
	return 0;
}

BZOJ 2732([HNOI2012]射箭-半平面交)

2732: [HNOI2012]射箭

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 186  Solved: 104
[Submit][Status][Discuss]

Description

沫沫最近在玩一个二维的射箭游戏,如下图 1 所示,这个游戏中的 x 轴在地面,第一象限中有一些竖直线段作为靶子,任意两个靶子都没有公共部分,也不会接触坐标轴。沫沫控制一个位于(0,0)的弓箭手,可以朝 0 至 90?中的任意角度(不包括 0度和 90度),以任意大小的力量射出带有穿透能力的光之箭。由于游戏中没有空气阻力,并且光之箭没有箭身,箭的轨迹会是一条标准的抛物线,被轨迹穿过的所有靶子都认为被沫沫射中了,包括那些 只有端点被射中的靶子。这个游戏有多种模式,其中沫沫最喜欢的是闯关模式。在闯关模式中,第一关只有一个靶
子,射中这个靶子即可进入第二关,这时在第一关的基础上会出现另外一个靶子,若能够一箭 双雕射中这两个靶子便可进入第三关,这时会出现第三个靶子。依此类推,每过一关都会新出 现一个靶子,在第 K 关必须一箭射中前 K 关出现的所有 K 个靶子才能进入第 K+1 关,否则游戏 结束。沫沫花了很多时间在这个游戏上,却最多只能玩到第七关“七星连珠”,这让她非常困惑。 于是她设法获得了每一关出现的靶子的位置,想让你告诉她,最多能通过多少关

Input

输入文件第一行是一个正整数N,表示一共有N关。接下来有N行,第i+1行是用空格隔开的三个正整数xi,yi1,yi2(yi1<yi2 ),表示第i关出现的靶子的横坐标是xi,纵坐标的范围是从yi1到yi2 。 
 输入保证30%的数据满足N≤100,50%的数据满足N≤5000,100%的数据满足N≤100000且给 出的所有坐标不超过109 。 
 

Output

仅包含一个整数,表示最多的通关数。 

Sample Input

5


2 8 12

5 4 5

3 8 10

6 2 3

1 3 7

Sample Output

3

HINT

先把方程写出来,改一改。
然后在坐标系上做半平面交。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<functional>
#define MAXN (100000+10)
#define MAXCi (1000000000)
#define eps 1e-15
#define For(i,n) for(int i=1;i<=n;i++)
using namespace std;
int n;
struct P
{
	double x,y;
	P(){}
	P(double _x,double _y):x(_x),y(_y){}
}p[MAXN*4];
struct V
{
	double x,y;
	V(){}
	V(double _x,double _y):x(_x),y(_y){}
	V(P a,P b):x(b.x-a.x),y(b.y-a.y){}
	friend double operator*(V a,V b){return a.x*b.y-a.y*b.x;}
	friend V operator*(double a,V b){return V(a*b.x,a*b.y);}
	friend P operator+(P a,V b){return P(a.x+b.x,a.y+b.y);}
};
struct line
{
	P p;
	V v;
	double ang;
	int i;
	line(){}
	line(double _x,double _y,double _a,double _b,int _i):p(P(_x,_y)),v(V(_a,_b)),i(_i){ang=atan2(v.y,v.x);}
	bool onleft(P A)
	{
		return v*V(p,A)>=0;
	}
	bool operator<(const line& l) const
	{
		return ang<l.ang;
	}
	friend P getinter(line a,line b)
	{
		/*
		V u=V(a.p,b.p);
		double t=(a.v*u)/(b.v*a.v);
		return b.p+(t*b.v);
		double s1=V(a.p,b.p+b.v)*a.v;
		double s2=a.v*V(a.p,b.p);
		return b.p+(-s1/(s1+s2))*b.v;
//		V u=V(a.p,b.p);
		*/
		V u=V(b.p,a.p);
		double t=(b.v*u)/(a.v*b.v);
		return a.p+t*a.v;

	}
}que[MAXN*10],q[MAXN*4];
int size=0;
int half_intersection(line *l,int n)
{
	int first=1,last=0;
	for (int i=1;i<=size;i++)
	{
		if (l[i].i>n) continue;
		if (!last) {q[++last]=l[i];continue;}
		while (first<last&&!l[i].onleft(p[last-1])) last--;
		while (first<last&&!l[i].onleft(p[first])) first++;
		if (fabs(q[last].v*l[i].v)<=eps)
		{
			if (q[last].onleft(l[i].p)) q[last]=l[i];
		}
		else q[++last]=l[i];
		if (first<last) p[last-1]=getinter(q[last-1],q[last]);
	}
	bool flag=1;
	while (flag)
	{
		flag=0;
		while (first<last&&!q[first].onleft(p[last-1])) last--,flag=1;
		while (first<last&&!q[last].onleft(p[first])) first++,flag=1;
	}
	/*
	p[last]=getinter(q[last],q[first]);
	for (int i=first;i<=last;i++)
		printf("%lf %lfn",p[i].x,p[i].y);
	cout<<endl;
	*/
	return last-first>1;
}
void pri(line a,line b)
{
	P c=getinter(a,b);
	printf("%.lf %.lfn",c.x,c.y);
}
int main()
{
	freopen("archery.in","r",stdin);
	freopen("archery.out","w",stdout);
	cin>>n;
	que[1]=line(0,0,0,1,0);
	que[2]=line(-1,0,1,0,0);
	que[3]=line(0,MAXCi,-1,0,0);
	que[4]=line(-MAXCi,MAXCi,0,-1,0);size=4;
//	pri(que[3],que[4]);
//	size=0;
	for (int i=1;i<=n;i++)
	{
		double x,l,r;
		scanf("%lf%lf%lf",&x,&l,&r);
		que[++size]=line(0,l/x,1/x,-1,i);
		que[++size]=line(0,r/x,-1/x,1,i);
	}
	sort(que+1,que+1+size);
//	cout<<size<<endl;
//	for (int i=l;i<=r;i++) cout<<halsf_intersection(que,i)<<' ';
//	cout<<half_intersection(que,50001);
	int l=0,r=n,Mid=0;
	while (l<=r)
	{
		int mid=(l+r)>>1;
		if (half_intersection(que,mid)) {Mid=mid;l=mid+1;}else r=mid-1;
	}
	cout<<Mid<<endl;
	return 0;
}

BZOJ 2241([SDOI2011]打地鼠-二分判断+贪心)

Description

打地鼠是这样的一个游戏:地面上有一些地鼠洞,地鼠们会不时从洞里探出头来很短时间后又缩回洞中。玩家的目标是在地鼠伸出头时,用锤子砸其头部,砸到的地鼠越多分数也就越高。

游戏中的锤子每次只能打一只地鼠,如果多只地鼠同时探出头,玩家只能通过多次挥舞锤子的方式打掉所有的地鼠。你认为这锤子太没用了,所以你改装了锤子,增加了锤子与地面的接触面积,使其每次可以击打一片区域。如果我们把地面看做M*N的方阵,其每个元素都代表一个地鼠洞,那么锤子可以覆盖R*C区域内的所有地鼠洞。但是改装后的锤子有一个缺点:每次挥舞锤子时,对于这R*C的区域中的所有地洞,锤子会打掉恰好一只地鼠。也就是说锤子覆盖的区域中,每个地洞必须至少有1只地鼠,且如果某个地洞中地鼠的个数大于1,那么这个地洞只会有1只地鼠被打掉,因此每次挥舞锤子时,恰好有R*C只地鼠被打掉。由于锤子的内部结构过于精密,因此在游戏过程中你不能旋转锤子(即不能互换RC)。

你可以任意更改锤子的规格(即你可以任意规定RC的大小),但是改装锤子的工作只能在打地鼠前进行(即你不可以打掉一部分地鼠后,再改变锤子的规格)。你的任务是求出要想打掉所有的地鼠,至少需要挥舞锤子的次数。

Hint:由于你可以把锤子的大小设置为1*1,因此本题总是有解的。

Input

 第一行包含两个正整数MN

下面M行每行N个正整数描述地图,每个数字表示相应位置的地洞中地鼠的数量。

Output

输出一个整数,表示最少的挥舞次数。

Sample Input

3 3



1 2 1



2 4 2



1 2 1



Sample Output



4



【样例说明】



使用2*2的锤子,分别在左上、左下、右上、右下挥舞一次。



【数据规模和约定】





对于100%的数据,1<=M,N<=100,其他数据不小于0,不大于10^5

二分判断+贪心

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<iostream>
#define MAXN (100+10)
#define INF (1000000000)
#define For(i,n) for(int i=1;i<=n;i++)
using namespace std;
int n,m,a[MAXN][MAXN],a2[MAXN][MAXN];
int is_ok(int l,int r)
{
	memcpy(a2,a,sizeof(a));
	int sum=0;
	For(i,n-l+1) For(j,m-r+1)
		if(a2[i][j])
		{
			int delta=a2[i][j];sum+=delta;
			for (int k=i;k<=i+l-1;k++)
				for (int k2=j;k2<=j+r-1;k2++)
					if (a2[k][k2]<delta) return 0;
					else a2[k][k2]-=delta;
		}
	return sum;
}
int main()
{
	scanf("%d%d",&n,&m);
	int cnt=0,ans=0;
	For(i,n) For(j,m) {scanf("%d",&a[i][j]);cnt+=a[i][j];}
	For(i,n) For(j,m)
	{
		if (i*j<ans) continue;
		if (!(cnt%(i*j))&&is_ok(i,j)*i*j==cnt)
		{
			ans=i*j;
		}
	}
	cout<<cnt/ans<<endl;
	return 0;
}