2022牛客寒假算法基础集训营4

比赛链接

2022牛客寒假算法基础集训营4

D.雪色光晕

题目描述

给出两个初始点,一个点动 \(n\) 次,另外一个点不动,给出动点的方向向量 \((dx,dy)\),求动点和静点的最短距离

输入描述:

第一行是一个正整数 \(n\)
第二行是四个整数 \(x_0\)\(y_0\)\(x\)\(y\) ,用空格隔开。用来表示动点和静点的初始坐标。
接下来的\(n\)行,每行输入两个整数 \(x_i\)\(y_i\),用来表示方向向量坐标。

数据范围:

\(1\leq n\leq 200000\)
\(10^9\leq x,y,x_i,y_i\leq10^9\)

输出描述:

最近的距离。如果你的答案和正确答案的相对误差不超过 \(10^{-7}\),则认为答案正确。

示例1

输入

1
1 1 1 2
2 0

输出

1.0

示例2

输入

2
0 0 1 1
1 0
1 1

输出

0.70710678

示例3

输入

1
-10 0 1 1
20 0

输出

1.0

解题思路

计算几何
点到直线的距离公式:
设直线 \(L\) 的方程为\(Ax+By+C=0\),点 \(P\) 的坐标为\((x_0,y_0)\),则点 \(P\) 到直线 \(L\) 的距离为:\(\frac{Ax_0+By_0+C}{A^2+B^2}\)
同理可知,当\(P(x0,y0)\),直线\(L\)的解析式为\(y=kx+b\)时,则点\(P\)到直线\(L\)的距离为:\(\frac{|kx_0-y_0+b|}{\sqrt{k^2+1}}\)

求出静点到 \(n\) 个线段的最小值即可

  • 时间复杂度:\(O(n)\)

代码

// Problem: 雪色光晕
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/23479/D
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

double x,y,xx,yy;
int n;
double dis(double x,double y,double xx,double yy)
{
	return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
double f(double dx,double dy)
{
	double xe=x+dx,ye=y+dy;
	double res=min(dis(x,y,xx,yy),dis(xe,ye,xx,yy));
	double k=dy/dx,b=y-k*x,bb=yy+1/k*xx;
	double xj=(bb-k*b)/(k*k+1);
	if(xj>=min(x,xe)&&xj<=max(x,xe))res=min(res,fabs(k*xx-yy+b)/sqrt(k*k+1));
	return res;
}
int main()
{
	scanf("%d",&n);
	scanf("%lf%lf%lf%lf",&x,&y,&xx,&yy);
    double res=dis(x,y,xx,yy);
	while(n--)
	{
		double dx,dy;
		scanf("%lf%lf",&dx,&dy);
		res=min(res,f(dx,dy));
		x+=dx,y+=dy;
	}
	printf("%lf",res);
    return 0;
}

G.子序列权值乘积

题目描述

小红定义一个数组的权值为该数组的最大值乘以最小值。例如数组 \([4,1,3]\) 的权值是 \(4*1=4\)
小红拿到了一个数组。她想知道,这个数组的所有 非空子序列 的权值的乘积是多少?由于该数过大,请对\(1000000007\)取模。
子序列的定义:对于一个数组,删除其中某些数之后(也可以不删)得到的数组。子序列中的数的相对顺序必须和原数组中的顺序相同。
例如:数组 \([1,3,2]\) 的非空子序列有 \([1] [3] [2] [1,3] [1,2] [3,2] [1,3,2]\)\(7\)个。

输入描述:

第一行输入一个正整数 \(n\) ,代表数组的长度。
第二行输入 \(n\) 个正整数 \(a_i\),代表小红拿到的数组。

数据范围:

\(1\leq n \leq 200000\)
\(1\leq a_i \leq 10^9\)

输出描述:

数组所有子序列的权值的乘积。答案对\(1000000007\)取模。

示例1

输入

2
2 3

输出

216

说明

对于子序列 \([2,3]\) ,其权值为 \(3 * 2 = 6\)
对于子序列 \([2]\) ,其权值为 \(2 * 2 = 4\)
对于子序列 \([3]\) ,其权值为 \(3 * 3 = 9\)
因此所有非空子序列的权值乘积为: $6 * 4 * 9 = 216¥

示例2

输入

4
4 1 4 3

输出

757784222

解题思路

欧拉降幂:若正整数 \(a,n\) 互质,则对于任何正整数 \(b\),有 \(a^b\equiv a^{b\bmod \varphi {(n)}}(\bmod n)\)

由于是子序列,我们考虑每个值对答案的贡献,将数组排序并不会影响答案,对某个值 \(a_i\),其作为最小值时,后面的数即更大的数不能取否则与最小值矛盾,前面的数可以任取,即有 \(i-1\) 个数可取可不取,共 \(2^{i-1}\) 种方案,同理,后面 \(n-i\) 个数有 \(2^{n-i}\) 种方案,共 \(2^{i-1}+2^{n-i}\) 种方案,其对答案的贡献为 \(a_i^{2^{i-1}+2^{n-i}}\),单单算的话由于指数过大会超时,可以欧拉降幂: \(a_i^{2^{i-1}+2^{n-i}}=a_i^{(2^{i-1}+2^{n-i})\bmod \varphi {(1e9+7)}}\)

  • 时间复杂度:\(O(n(logn+log(1e9+7)))\)

代码

// Problem: 子序列权值乘积
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/23479/G
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=2e5+5,mod=1e9+7;
int n,a[N];
int ksm(int a,int b,int p)
{
	int res=1%p;
	for(;b;b>>=1)
	{
		if(b&1)res=1ll*res*a%p;
		a=1ll*a*a%p;
	}
	return res;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    int res=1;
    for(int i=1;i<=n;i++)
    	res=(1ll*res*ksm(a[i],(ksm(2,i-1,mod-1)+ksm(2,n-i,mod-1))%(mod-1),mod))%mod;
    printf("%d",res);
    return 0;
}

I.爆炸的符卡洋洋洒洒

题目描述

给出 \(n\) 个物品体积 \(a_i\) 和价值 \(b_i\),各物品最多只能选择一次,要求选出的物品的体积为 \(k\) 的倍数,求最大价值

输入描述:

第一行输入两个正整数 \(n\)\(k\) ,用空格隔开。
接下来的 \(n\) 行,每行输入两个正整数 \(a_i\)\(b_i\),用空格隔开。

数据范围:

\(1\leq n,k \leq 1000\)
\(1\leq a_i,b_i \leq 10^9\)

输出描述:

体积无法形成倍数关系,则输出\(-1\)
否则输出最大价值。

示例1

输入

2 3
1 2
2 1

输出

3

示例2

输入

2 2
1 2
2 1

输出

1

示例3

输入

3 4
1 2
5 3
1 4

输出

-1

解题思路

01背包变形

背包体积过大,但是 \(k\) 较小,可将体积对 \(k\) 取模,将体积转化为模,将要求的体积转化为模为 \(0\),即:

  • 状态表示:\(f[i][j]\) 表示前 \(i\) 个物品体积对 \(k\) 取模为 \(j\) 时的最大价值
  • 状态计算:\(f[i][j]=max(f[i-1][j],f[i-1][(j-v[i]\%k+k)\%k]+w[i])\)

注意:初始化要尽可能小,表示取不到,\(f[0][0]\) 表示 \(0\) 个物品价值为 \(0\)

  • 时间复杂度:\(O(nk)\)

代码

// Problem: 爆炸的符卡洋洋洒洒
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/23479/I
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=1010;
LL f[N][N];
int n,k,v[N],w[N];
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)scanf("%d%d",&v[i],&w[i]);
    for(int i=0;i<=n;i++)for(int j=0;j<=k;j++)f[i][j]=-1e12;
    f[0][0]=0;
    for(int i=1;i<=n;i++)
    	for(int j=0;j<k;j++)
            f[i][j]=max(f[i-1][j],f[i-1][(j-v[i]%k+k)%k]+w[i]);
    printf("%lld",f[n][0]<=0?-1:f[n][0]);
    return 0;
}

J.区间合数的最小公倍数

题目描述

给定一个区间 \([l,r]\),求其中所有合数的最小公倍数

输入描述:

两个正整数 \(l\)\(r\) ,用空格隔开。

数据范围:\(1\leq l \leq r \leq 30000\)

输出描述:

若区间 \([l,r]\) 中没有任何合数,则输出\(-1\)
否则输出区间 \([l,r]\) 所有合数的最小公倍数,答案对\(1000000007\)取模。

示例1

输入

2 3

输出

-1

说明

区间 \([2,3]\) 的两个数 \(2\)\(3\) 都是素数,不是合数。

示例2

输入

4 6

输出

12

说明

区间\([4,6]\)中有三个数:\(4、5、6\),其中 \(5\) 是素数, \(4\)\(6\) 是合数,它们的最小公倍数是\(12\)

示例3

输入

25000 30000

输出

187554966

解题思路

数学:求取多个数的最小公倍数,即将所有数分解质因数,统计每个素数出现的最高次幂,其乘积即为所求
例:\(a=2^3*3^5*5^1,b=2^4*3^1*5^2\),那么它们的最小公倍数就是 \(2^4*3^5*5^2\)

直接欧拉筛求素数,剩余的合数求最小公倍数即可

  • 时间复杂度:\(O(nlogn)\)

代码

// Problem: 区间合数的最小公倍数
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/23479/J
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=3e4+5,mod=1e9+7;
bool st[N];
int prime[N],v[N];
int l,r,m;
void primes(int n)
{
	memset(v,0,sizeof v);
	for(int i=2;i<=n;i++)
	{
		if(v[i]==0)
		{
			prime[++m]=i;
			v[i]=i;
			st[i]=true;
		}
		for(int j=1;j<=m;j++)
		{
			if(i*prime[j]>n||v[i]<prime[j])break;
			v[i*prime[j]]=prime[j];
		}
	}
}
int ksm(int a,int b,int p)
{
	int res=1;
	for(;b;b>>=1)
	{
		if(b&1)res=1ll*res*a%p;
		a=1ll*a*a%p;
	}
	return res;
}
int main()
{
	primes(N);	
    scanf("%d%d",&l,&r);
    int res=1;
    unordered_map<int,int> mp;
    for(int i=l;i<=r;i++)
    {
    	if(!st[i])
    	{
    		int j=i;
    		for(int k=2;k*k<=j;k++)
    		{
    			if(j%k==0)
    			{
    				int t=0;
    				while(j%k==0)t++,j/=k;
    				mp[k]=max(mp[k],t);
    			}
    		}
    		if(j>1)mp[j]=max(mp[j],1);
    	}
    }
    for(auto [x,y]:mp)res=1ll*res*ksm(x,y,mod)%mod;
    printf("%d",res==1?-1:res);
    return 0;
}
posted @ 2022-02-08 23:07  zyy2001  阅读(94)  评论(0编辑  收藏  举报