J - Programming Tutors
题目大意:给n个学生和n个教练的坐标,按对分配,使得学生和教练的最大距离最小,求出这个最大距离的最小值。
思路:最大距离的最小值可以通过二分求得,之前接触过了。那么如何二分出一个最大距离的最小值需要用二分图最大匹配,最大匹配返回的是可以匹配的个数,如果个数是n的话,说明可以匹配,继续缩小版边界,最后二分的结果就是满足最大匹配的最小值。
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<math.h>
#include<vector>
#include<map>
#include<queue>
#define int long long
const int N=300,INF=0x3f3f3f3f,mod=1e9+7;
using namespace std;
struct node
{
int x,y;
} s[N],t[N];
int n;
int g[N][N];
int match[N];
bool st[N];
int mid;
bool find(int x)
{
for(int i=1;i<=n;i++)
{
if(g[i][x]<=mid&&!st[i])
{
st[i]=true;
if(match[i]==0||find(match[i]))
{
match[i]=x;
return true;
}
}
}
return false;
}
int check()
{
int cnt=0;
for(int i=1;i<=n;i++)
{
memset(st,0,sizeof st);
if(find(i))cnt++;
}
return cnt;
}
signed main()
{
cin>>n;
for(int i=1; i<=n; i++)
{
int a,b;
cin>>a>>b;
s[i]= {a,b};
}
for(int i=1; i<=n; i++)
{
int a,b;
cin>>a>>b;
t[i]= {a,b};
}
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
g[i][j]=abs(s[i].x-t[j].x) + abs(s[i].y-t[j].y);
}
}
int l=0,r=INF;
while(l<r)
{
memset(match,0,sizeof match);
mid=l+r>>1;
if(check()==n)r=mid;
else l=mid+1;
}
cout<<l<<endl;
}
L - Sticky Situation
题意:给定一个 n ,和 n 个数, 看存不存在三个数可以构成三角形。
思路:先从小到大排序,原因是可以得到性质。
对于有相对顺序的三个数字
a
i
,
a
k
,
a
m
(
i
<
k
<
m
)
a_i,a_k,a_m(i<k<m)
ai,ak,am(i<k<m),因为m是最大的数,所以
a
i
<
a
m
+
a
k
a_i<a_m+a_k
ai<am+ak,
a
k
<
a
m
+
a
i
a_k<a_m+a_i
ak<am+ai 显然成立。对于
a
i
+
a
k
>
a
m
a_i+a_k>a_m
ai+ak>am这种情况,我们希望它尽可能成立,这时候希望
a
m
a_m
am尽可能小,
a
i
+
a
k
a_i+a_k
ai+ak尽可能大,但仍要满足下标关系,所以每次只需要判定
a
i
+
a
i
+
1
与
a
i
+
2
a_i+a_{i+1}与a_{i+2}
ai+ai+1与ai+2的关系即可以判定答案是否存在。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
typedef long long LL;
LL a[N];
int main()
{
LL n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n);
for(int i=1;i<=n;i++)
{
if(a[i]+a[i+1]>a[i+2]&&a[i+1]+a[i+2]>a[i]&&a[i]+a[i+2]>a[i+1])
{
cout<<"possible";
return 0;
}
}
cout<<"impossible";
}
I - Older Brother
题意:给定一个
q
q
q ,
q
q
q 的范围如下图所示:
要你判断这个 q 是不是某个质数 p 的某个次方,即判断
q
=
p
k
(
k
>
=
1
)
q=p^k( k>=1)
q=pk(k>=1) 是否成立。
思路:首先当
k
=
1
k=1
k=1的时候直接输出
y
e
s
yes
yes, 即 当
q
q
q 是一个素数。如果当
q
q
q 不是素数可以筛出
1
1
1 到
n
n
n 之前所有的素数,然后枚举其所有的指数并标记,看
q
q
q 是不是某个质数的某个次方的值。
但是从q的范围来看不能枚举出1~q之前所有的素数,这时候我们可以抓住一点性质,如果判断一个数是否为质数只需要
n
\sqrt{n}
n 的时间复杂度,即
k
=
1
的
情
况
很
容
易
判
断
k=1的情况很容易判断
k=1的情况很容易判断,对于
k
>
=
2
k>=2
k>=2 的情况,最大的质数也只会在
1
e
9
\sqrt{1e9}
1e9的范围筛掉
1
e
9
1e9
1e9 的 q ,所以这种情况只需要枚举
1
e
9
\sqrt{1e9}
1e9范围内的质数,再指数枚举它的次方即可,复杂度大概是
n
⋅
l
o
g
n
\sqrt{n}·log_n
n⋅logn。
#include <iostream>
#include <algorithm>
using namespace std;
const int N= 1e5+10;
const int M= 1e9+10;
typedef long long LL;
int primes[N], cnt;
bool st[N];
bool vis[N];
bool is_prime(int x)
{
if (x < 2) return false;
for (int i = 2; i <= x / i; i ++ )
if (x % i == 0)
return false;
return true;
}
void get_primes(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] <= n / i; j ++ )//
{
st[primes[j] * i] = true;//如果下面i % primes[j] != 0,因为primes[j]
//从小到大枚举,所以primes[j]< i 的最小质因子,所以primes[j]是primes[j]* i 的最小值因子
if (i % primes[j] == 0) break;
//如果i % primes[j] == 0,因为primes[j]是从小到大枚举的
// 那么primes[j]一定是i的最小质因子,也是primes[j]* i 的最小值因子
}
}
}
int main()
{
int n;
cin >> n;
if(is_prime(n)) cout<<"yes";
else
{
get_primes(N);
for(int i=0;i<cnt;i++)
{
//cout<<primes[i]<<endl;
for(LL j=primes[i];j<=M;j*=primes[i])
{
if(n==j) {cout<<"yes";return 0;}
}
}
cout<<"no";
}
return 0;
}
C - Brexit
题意:给 n , m , st , ed。 代表n个点,m条边,自己所在的城市st, 开始沦陷的城市ed,沦陷的城市周围有许多邻城,如果对于某个城市来说,如果周围沦陷的城市大于等于自身周围城市个数的一半,那么自身这个城市也将沦陷,也可以理解为被攻占。判断最后自己所在的城市有没有沦陷。
思路:开两个数组,一个村邻城个数,一个存沦陷的邻城个数。存边的时候把每个点的邻城出度记录一下,以便查询到每一个点的邻城有多少。
d
f
s
dfs
dfs 遍历所有沦陷的城市,告诉这个城市周围所有的城市,让邻城存沦陷的邻城的那个数组
+
+
++
++ 。
#include <iostream>
#include<cstring>
using namespace std;
const int N = 3e5+10;
int vis[N];//遍历过的点不再遍历
typedef long long LL;
int e[2*N],ne[2*N],h[2*N],idx;
int g[N],g1[N];//周围的邻城个数,周围沦陷的城市个数
void add(int a, int b) // 添加一条边a->b
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ,g[a]++;
}
LL n,m,st,le;
void dfs(int x)
{
vis[x]=1;//标记所有的沦陷的城市
for(int i=h[x];i!=-1;i=ne[i])
{
int j=e[i];
if(vis[j]) continue;
g1[j]++;//告诉所有的邻城 我被攻占了
if(g1[j]*2>=g[j])//判断这些邻城,看看他们是否跟着沦陷
{
dfs(j);//如果满足条件就递归下去
}
}
return ;
}
int main()
{
memset(h, -1, sizeof h);
scanf("%ld%ld%ld%ld",&n,&m,&st,&le);
while (m -- )
{
LL a,b;
scanf("%lld%lld", &a, &b);
add(a,b);
add(b,a);
}
dfs(le);
if(!vis[st]) printf("stay");
else printf("leave");
}