Codeforces Round #284 (Div. 2)

本文解析了CF竞赛中的两道题目:C题“疯狂城市”要求计算从家到大学的最少穿越街区步数;E题“数组与操作”探讨如何通过特定操作最大化操作次数,涉及二分图与匈牙利算法。

        好久没打CF,最近又开始忙里偷闲,结果就掉到DIV2了。。。  而且这次做了三题结果还被hack了一道。。。

A和B就不说了

C. Crazy Town
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Crazy Town is a plane on which there are n infinite line roads. Each road is defined by the equation aix + biy + ci = 0, where ai and biare not both equal to the zero. The roads divide the plane into connected regions, possibly of infinite space. Let's call each such region a block. We define an intersection as the point where at least two different roads intersect.

Your home is located in one of the blocks. Today you need to get to the University, also located in some block. In one step you can move from one block to another, if the length of their common border is nonzero (in particular, this means that if the blocks are adjacent to one intersection, but have no shared nonzero boundary segment, then it are not allowed to move from one to another one in one step).

Determine what is the minimum number of steps you have to perform to get to the block containing the university. It is guaranteed that neither your home nor the university is located on the road.

Input

The first line contains two space-separated integers x1y1 ( - 106 ≤ x1, y1 ≤ 106) — the coordinates of your home.

The second line contains two integers separated by a space x2y2 ( - 106 ≤ x2, y2 ≤ 106) — the coordinates of the university you are studying at.

The third line contains an integer n (1 ≤ n ≤ 300) — the number of roads in the city. The following n lines contain 3 space-separated integers ( - 106 ≤ ai, bi, ci ≤ 106|ai| + |bi| > 0) — the coefficients of the line aix + biy + ci = 0, defining the i-th road. It is guaranteed that no two roads are the same. In addition, neither your home nor the university lie on the road (i.e. they do not belong to any one of the lines).

Output

Output the answer to the problem.

Sample test(s)
input
1 1
-1 -1
2
0 1 0
1 0 0
output
2
input
1 1
-1 -1
3
1 0 0
0 1 0
1 1 -3
output
2
Note

Pictures to the samples are presented below (A is the point representing the house; B is the point representing the university, different blocks are filled with different colors):



一道几何题,挺容易的结果想太多搞错了,其实就是等价于判断对于每一条直线,A和B两点是否在直线的两端。只要将点的坐标带入直线方程,判断值是否异号即可。
#include<queue>  
#include<cstdio>  
#include<cstring>  
#include<algorithm>  
#include<iostream>
#include<cmath>
#include<set>
#include<stack>
#include<string>
#include<cstdio>
#include<map>
using namespace std;  
#define ll  long long
struct node{ll a,b,c;};
int n;
node s,t, nd;
bool f(node x){
	return (nd.a*s.a+nd.b*s.b+nd.c>0)^(nd.a*t.a+nd.b*t.b+nd.c>0);
}
int main()
{
	cin>>s.a>>s.b>>t.a>>t.b;
	cin>>n;
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		cin>>nd.a>>nd.b>>nd.c;
		if(f(nd))ans++;
	}
	cout<<ans<<endl;

}



E. Array and Operations
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

You have written on a piece of paper an array of n positive integers a[1], a[2], ..., a[n] and m good pairs of integers (i1, j1), (i2, j2), ..., (im, jm). Each good pair (ik, jk) meets the following conditions: ik + jk is an odd number and 1 ≤ ik < jk ≤ n.

In one operation you can perform a sequence of actions:

  • take one of the good pairs (ik, jk) and some integer v (v > 1), which divides both numbers a[ik] and a[jk];
  • divide both numbers by v, i. e. perform the assignments:  and .

Determine the maximum number of operations you can sequentially perform on the given array. Note that one pair may be used several times in the described operations.

Input

The first line contains two space-separated integers nm (2 ≤ n ≤ 1001 ≤ m ≤ 100).

The second line contains n space-separated integers a[1], a[2], ..., a[n] (1 ≤ a[i] ≤ 109) — the description of the array.

The following m lines contain the description of good pairs. The k-th line contains two space-separated integers ikjk (1 ≤ ik < jk ≤ n,ik + jk is an odd number).

It is guaranteed that all the good pairs are distinct.

Output

Output the answer for the problem.

Sample test(s)
input
3 2
8 3 8
1 2
2 3
output
0
input
3 2
8 12 8
1 2
2 3
output
2


用到了二分图,匈牙利算法。来自百度的介绍:
在介绍 匈牙利算法之前还是先提一下几个概念,下面M是G的一个匹配。
M-交错路:p是G的一条通路,如果p中的边为属于M中的边与不属于M但属于G中的边交替出现,则称p是一条M-交错路。如:路径(X3,Y2,X1,Y4),(Y1,X2,Y3)。
M-饱和点:对于v∈V(G),如果v与M中的某条边关联,则称v是M-饱和点,否则称v是非M-饱和点。如X1,X2,Y1,Y2都属于M-饱和点,而其它点都属于非M-饱和点。
M-可 增广路:p是一条M-交错路,如果p的起点和终点都是非M-饱和点,则称p为M-可增广路。如(X3,Y2,X1,Y4)。(不要和流网络中的 增广路径弄混了)
求最大 匹配的一种 显而易见的算法是:先找出全部匹配,然后保留匹配数最多的。但是这个算法的 时间复杂度为边数的指数级 函数。因此,需要寻求一种更加高效的算法。下面介绍用 增广路求最大 匹配的方法(称作 匈牙利算法匈牙利 数学家Edmonds于1965年提出)。
增广路的定义(也称增广轨或交错轨):
若P是图G中一条连通两个未 匹配顶点的路径,并且属于M的边和不属于M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条 增广路径。
增广路的定义可以推出下述三个结论:
1-P的路径个数必定为奇数,第一条边和最后一条边都不属于M。
2-将M和P进行取反操作可以得到一个更大的 匹配M’。
3-M为G的最大 匹配当且仅当不存在M的 增广路径。
算法轮廓:
⑴置M为空
⑵找出一条 增广路径P,通过异或操作获得更大的 匹配M’代替M
⑶重复⑵操作直到找不出 增广路径为止
具体实现:
bool f(int u){
	for(int i=0;i<edge[u].size();i++){
		int v=edge[u][i];
		if(vis[v])continue;
		vis[v]=1;
		if(pei[v]==0||f(pei[v])){
			pei[v]=u;
			return 1;
		}
	}
	return 0;
}

int main()
{
   for(int i=1;i<=n;i++)
  {
     for(int j=1;j<=m;j++)
         vis[j]=0;
      if(f(i)){
        ans+=1;
    }

  }
}

将每个点的value分解成每个质数因子,所有质数因子的集合构成了二分图的顶点。然后建立边,遍历m个pair,每个pair中有两个质数因子的集合,然后两个集合中的每个点对分别判断是否该连一条边。
#include<queue>  
#include<cstdio>  
#include<cstring>  
#include<algorithm>  
#include<iostream>
#include<cmath>
#include<set>
#include<stack>
#include<string>
#include<cstdio>
#include<map>
using namespace std;  
#define ll  long long
int n,m,x,y;
vector<int>v[101];
int num[101];
int p[2][2001],id[2];
vector<int>edge[2001];
int vis[2001];
int pei[2001];
bool f(int u){
	for(int i=0;i<edge[u].size();i++){
		int v=edge[u][i];
		if(vis[v])continue;
		vis[v]=1;
		if(pei[v]==0||f(pei[v])){
			pei[v]=u;
			return 1;
		}
	}
	return 0;
}


int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>x;
		int j=2;
		while(x>1&&j*j<=x){
			while(x%j==0)
				id[i%2]++,p[i%2][id[i%2]]=j,
				num[i]++,v[i].push_back(id[i%2]),x/=j;
			j++;
		}
		if(x>1)id[i%2]++,p[i%2][id[i%2]]=x,
			num[i]++,v[i].push_back(id[i%2]);
	}
	for(int i=1;i<=m;i++){
		cin>>x>>y;
		if(num[x]==0||num[y]==0)continue;
		for(int j=0;j<v[x].size();j++){
		   int le= p[x%2][v[x][j]];
		   for(int k=0;k<v[y].size();k++){
			   int ri=p[y%2][v[y][k]];
			   if(le!=ri)continue;
			   if(x%2)
			   edge[v[x][j]].push_back(v[y][k]);
			   else edge[v[y][k]].push_back(v[x][j]);
		   }
		}
	}
	int ans=0;
	for(int i=1;i<=id[1];i++){
		for(int j=1;j<=id[0];j++)
			vis[j]=0;
		if(f(i))ans++;
	}
	cout<<ans<<endl;
}





评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值