牛客练习赛65题解

题目链接

A.最值序列

题意:
n 个 数 , 进 行 乘 法 和 加 法 n个数,进行乘法和加法 n
要 求 乘 法 和 加 法 的 次 数 一 样 多 要求乘法和加法的次数一样多
题解:
选 n / 2 个 小 的 进 行 相 加 选n/2个小的进行相加 n/2
相 加 之 后 乘 后 n / 2 个 大 的 即 可 相加之后乘后n/2个大的即可 n/2
( 题 目 给 出 n 为 偶 数 , 我 当 时 没 看 见 , 代 码 考 虑 了 奇 数 情 况 ) (题目给出n为偶数,我当时没看见,代码考虑了奇数情况) (n)

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
#define SZ(x) (int)x.size()
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double,double> pdd;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[][2]={{0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1}};

ll a[maxn];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    sort(a+1,a+1+n);
    ll ans=0;
    int i=1;
    if(n&1)i++;
    for(;i<=n;i++){
        if(i<=(n+1)/2)ans=(ans+a[i])%mod;
        else ans=ans*a[i]%mod;
    }
    cout<<ans;
    return 0;
}



B.多重序列

题意:
n 个 组 , 每 组 m 个 数 n个组,每组m个数 nm
一 组 的 权 值 为 该 组 数 的 乘 积 一组的权值为该组数的乘积
求 最 大 权 值 m o d   1 e 9 + 7 求最大权值mod~1e9+7 mod 1e9+7
每 个 数 都 为 k 的 非 负 整 数 次 幂 每个数都为k的非负整数次幂 k
题解:
由 于 取 余 了 , 所 以 没 法 比 较 大 小 由于取余了,所以没法比较大小
这 个 时 候 最 后 一 句 话 很 关 键 这个时候最后一句话很关键
由 于 有 最 后 一 句 话 , m 个 数 就 可 以 化 成 由于有最后一句话,m个数就可以化成 m
k a 1 , k a 2 , … … , k a n k^{a_1},k^{a_2},……,k^{a_n} ka1,ka2,,kan
那 么 这 些 数 相 乘 就 是 把 他 们 的 指 数 相 加 那么这些数相乘就是把他们的指数相加
所 以 直 接 把 每 一 组 全 部 化 成 k 的 幂 形 式 , 指 数 相 加 比 大 小 所以直接把每一组全部化成k的幂形式,指数相加比大小 k
最 后 用 快 速 幂 算 出 答 案 最后用快速幂算出答案
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
#define SZ(x) (int)x.size()
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double,double> pdd;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[][2]={{0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1}};

ll n,m,k,p;
ll Pow(ll a, ll b){
	ll ans = 1;
	while(b > 0){
		if(b & 1){
			ans = ans * a % p;
		}
		a = a * a % p;
		b >>= 1;
	}
	return ans;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);

    cin>>n>>m>>k>>p;
    ll ans=0;
    if(k==1){cout<<1;return 0;}
    for(int i=1;i<=n;i++){
        ll tmp=0;
        for(int j=1;j<=m;j++){
            ll x;
            cin>>x;
            x/=k;
            while(x)x/=k,tmp++;
        }
        ans=max(ans,tmp);
    }
    cout<<Pow(k,ans);
    return 0;
}



C.二维动点

题意:
二 维 平 面 上 有 n 个 点 二维平面上有n个点 n
初 始 位 置 为 ( 0 , 0 ) , 可 以 选 择 任 意 一 个 非 当 前 位 置 给 出 的 点 初始位置为(0,0),可以选择任意一个非当前位置给出的点 (0,0)
把 这 个 点 和 初 始 位 置 连 成 直 线 , 可 以 到 达 直 线 中 的 任 意 一 个 位 置 把这个点和初始位置连成直线,可以到达直线中的任意一个位置 线线
这 个 位 置 的 坐 标 可 以 不 是 整 数 这个位置的坐标可以不是整数
询 问 q 次 , 最 少 多 少 次 到 达 ( x i , y i ) 询问q次,最少多少次到达(x_i,y_i) q(xi,yi)
题解:
把 样 例 画 一 遍 就 会 发 现 , 其 实 最 多 也 只 需 要 3 次 到 达 把样例画一遍就会发现,其实最多也只需要3次到达 3
然 后 直 接 对 每 个 答 案 进 行 分 析 然后直接对每个答案进行分析
如 果 ( 0 , 0 ) 和 某 个 点 的 直 线 直 接 能 到 ( x i , y i ) 如果(0,0)和某个点的直线直接能到(x_i,y_i) (0,0)线(xi,yi)
那 就 直 接 答 案 为 1 , 这 个 就 需 要 用 到 斜 率 那就直接答案为1,这个就需要用到斜率 1
但 为 了 防 止 卡 精 度 考 虑 用 s e t 和 g c d 但为了防止卡精度考虑用set和gcd setgcd
对 每 个 点 ( a i , b i ) 换 成 ( a i / g c d ( a , b ) , b i / g c d ( a , b ) ) 对每个点(a_i,b_i)换成(a_i/gcd(a,b),b_i/gcd(a,b)) (ai,bi)(ai/gcd(a,b),bi/gcd(a,b))
这 样 既 可 以 表 示 斜 率 , 又 可 以 防 止 卡 精 度 这样既可以表示斜率,又可以防止卡精度
如 果 询 问 的 时 候 有 这 样 的 点 存 在 , 说 明 答 案 为 1 如果询问的时候有这样的点存在,说明答案为1 1
然 后 开 始 分 析 不 存 在 的 情 况 然后开始分析不存在的情况
首 先 考 虑 − 1 , 根 本 到 不 了 首先考虑-1,根本到不了 1
如 果 所 有 点 到 ( 0 , 0 ) 都 是 共 线 的 , 那 么 肯 定 到 不 了 如果所有点到(0,0)都是共线的,那么肯定到不了 (0,0)线
这 种 时 候 只 有 一 种 斜 率 这种时候只有一种斜率
如 果 图 中 有 超 过 2 个 点 , 因 为 点 不 重 复 如果图中有超过2个点,因为点不重复 2
总 可 以 从 ( 0 , 0 ) 和 一 个 点 连 线 到 达 终 点 和 某 个 点 的 连 线 上 总可以从(0,0)和一个点连线到达终点和某个点的连线上 (0,0)线线
这 样 就 需 要 2 次 , 答 案 就 为 2 这样就需要2次,答案就为2 22
然 后 考 虑 3 次 的 情 况 , 这 个 时 候 只 有 2 个 点 的 情 况 然后考虑3次的情况,这个时候只有2个点的情况 32
可 以 发 现 , 如 果 这 ( 0 , 0 ) 到 一 个 点 和 ( x i , y i ) 到 另 一 个 点 平 行 可以发现,如果这(0,0)到一个点和(x_i,y_i)到另一个点平行 (0,0)(xi,yi)
就 需 要 多 去 走 一 次 , 找 相 交 的 时 刻 , 这 时 候 就 需 要 3 就需要多去走一次,找相交的时刻,这时候就需要3 3
其 实 就 是 一 个 平 行 四 边 形 , 并 且 对 角 为 ( 0 , 0 ) 和 ( x i , y i ) 其实就是一个平行四边形,并且对角为(0,0)和(x_i,y_i) (0,0)(xi,yi)
除 此 之 外 都 是 2 的 情 况 了 除此之外都是2的情况了 2

但 是 这 道 题 需 要 注 意 的 是 , 二 维 坐 标 的 点 可 能 给 出 ( 0 , 0 ) 但是这道题需要注意的是,二维坐标的点可能给出(0,0) (0,0)
但 其 实 这 个 点 是 没 有 用 的 , 如 果 可 以 走 一 开 始 就 用 了 但其实这个点是没有用的,如果可以走一开始就用了
否 则 如 果 再 想 用 ( 0 , 0 ) 至 少 需 要 3 步 , 那 就 算 不 用 ( 0 , 0 ) 也 可 以 到 达 了 否则如果再想用(0,0)至少需要3步,那就算不用(0,0)也可以到达了 (0,0)3(0,0)
所 以 直 接 把 ( 0 , 0 ) 去 掉 不 要 影 响 最 后 的 判 断 所以直接把(0,0)去掉不要影响最后的判断 (0,0)
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
#define SZ(x) (int)x.size()
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double,double> pdd;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[][2]={{0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1}};

ll a[maxn],xx[maxn],yy[maxn];
ll b[maxn];
bool ok(ll x,ll y){
    if((xx[1]-x)*yy[2]-(yy[1]-y)*xx[2])return 0;
    if((xx[2]-x)*yy[1]-(yy[2]-y)*xx[1])return 0;
    return 1;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n,q;
    cin>>n>>q;
    set<pii> s;
    for(int i=1;i<=n;i++){
        cin>>xx[i]>>yy[i];
        if(!xx[i]&&!yy[i]){i--,n--;continue;}
        int tmp=__gcd(xx[i],yy[i]);
        s.insert(mp(xx[i]/tmp,yy[i]/tmp));
    }
    while(q--){
        ll x,y;
        cin>>x>>y;
        if(!x&&!y){cout<<0<<endl;continue;}
        int tmp=__gcd(x,y);
        bool f=0;
        if(ok(x,y))f=1;
        x/=tmp,y/=tmp;
        if(s.count(mp(x,y)))cout<<1<<endl;
        else if(s.size()<=1)cout<<-1<<endl;
        else {
            if(n>2)cout<<2<<endl;
            else{
                if(f)cout<<3<<endl;
                else cout<<2<<endl;
            }
        }
    }
    return 0;
}



D. 最小公倍数

题意:
给 一 个 数 n , 把 n 拆 成 几 个 数 , 这 几 个 数 的 和 为 n 给一个数n,把n拆成几个数,这几个数的和为n nnn
使 得 这 个 数 的 子 序 列 ( 包 括 空 序 列 , l c m 为 1 ) 中 有 尽 量 多 的 l c m 使得这个数的子序列(包括空序列,lcm为1)中有尽量多的lcm 使(lcm1)lcm
求 出 这 个 最 大 值 m o d   1 e 9 + 7 求出这个最大值 mod~1e9+7 mod 1e9+7
题解:
拆 出 的 数 首 先 一 定 是 不 同 的 , 如 果 重 复 出 现 不 会 增 加 l c m 数 量 拆出的数首先一定是不同的,如果重复出现不会增加lcm数量 lcm
反 而 会 浪 费 n 的 一 部 分 值 反而会浪费n的一部分值 n
每 个 数 都 拆 成 质 数 的 幂 每个数都拆成质数的幂
并 且 质 数 的 幂 是 从 小 到 大 进 行 选 择 并且质数的幂是从小到大进行选择
因 为 如 果 某 一 个 数 是 b i a p ∗ b j a q 因为如果某一个数是b_i^{a_p}*b_j^{a_q} biapbjaq
你 完 全 可 以 把 他 拆 成 b i a p 和 b j a q 你完全可以把他拆成b_i^{a_p}和b_j^{a_q} biapbjaq
由 于 基 本 不 等 式 之 类 的 推 论 , 所 以 相 加 一 定 比 相 乘 花 费 小 由于基本不等式之类的推论,所以相加一定比相乘花费小
甚 至 贡 献 可 能 比 相 乘 大 甚至贡献可能比相乘大
所 以 就 成 了 一 个 选 素 数 幂 的 过 程 和 记 录 贡 献 所以就成了一个选素数幂的过程和记录贡献
先 进 行 素 数 筛 , 然 后 就 可 以 用 背 包 进 行 计 算 最 大 贡 献 先进行素数筛,然后就可以用背包进行计算最大贡献
因 为 出 现 的 这 样 出 现 的 l c m 都 是 不 会 重 复 的 因为出现的这样出现的lcm都是不会重复的 lcm
所 以 直 接 和 之 前 的 l c m 相 乘 就 是 新 的 贡 献 所以直接和之前的lcm相乘就是新的贡献 lcm
但 是 这 个 结 果 要 进 行 取 余 , 就 没 法 比 大 小 了 但是这个结果要进行取余,就没法比大小了
所 以 就 用 l o g 进 行 记 录 大 小 , l o g 相 加 即 里 面 的 数 相 乘 所以就用log进行记录大小,log相加即里面的数相乘 loglog
而 且 l o g 可 以 保 证 大 小 在 范 围 内 而且log可以保证大小在范围内 log
最 后 找 到 最 大 值 即 可 最后找到最大值即可
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
#define SZ(x) (int)x.size()
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<double, ll> pll;
typedef pair<double,double> pdd;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[][2]={{0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1}};

int prime[maxn];
bool isprime[maxn];
int num=0;
void Prime(int n){
    memset(isprime,true,sizeof(isprime));
    for(int i=2;i<=n;i++){
        if(isprime[i]) prime[++num]=i;
        for(int j=1;j<=num;j++){
            if(i*prime[j]>maxn) break;
            isprime[i*prime[j]]=false;
            if(i%prime[j]==0) break;
        }
    }
}
pll dp[maxn] ;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    Prime(100000);
    int n;
    ll sum=0;pll ans=mp(0,1);
    cin>>n;
    dp[0]=mp(0,1);
    for(int i=1;i<=n;i++){
        for(int j=n;j>=1;j--){
            ll x,y,k=1;
            x=y=prime[i];
            while(x<=j){
                dp[j]=max(dp[j],mp(dp[j-x].fi+log2(k+1),dp[j-x].se*(k+1)%mod));
                y*=prime[i],k++,x+=y;
            }
            ans=max(ans,dp[j]);
        }
        sum+=prime[i];
        if(sum>n)break;
    }
    cout<<ans.se;
    return 0;
}



E.游走配对

题意:
共 有 n 个 点 , 第 i 个 点 的 点 权 为 a i , 还 有 一 个 b i 共有n个点,第i个点的点权为a_i,还有一个b_i niai,bi
每 多 一 次 经 过 第 i 个 点 点 权 就 要 加 b i 每多一次经过第i个点点权就要加b_i ibi
即 第 k 次 经 过 i 节 点 的 权 值 为 a i + ( k − 1 ) ∗ b i 即第k次经过i节点的权值为a_i + (k - 1)*b_i kiai+(k1)bi
其 中 有 m 条 路 从 u i 到 v i 其中有m条路从u_i到v_i muivi
有 q 个 x i , y i 选 择 q 对 从 x i 到 y i , 代 价 为 路 径 点 权 和 有q个x_i,y_i选择q对从x_i到y_i,代价为路径点权和 qxi,yiqxiyi
求 最 小 的 代 价 求最小的代价
题解:
q 个 x i 到 任 一 q 个 里 选 一 个 的 y i q个x_i到任一q个里选一个的y_i qxiqyi
直 接 就 可 以 考 虑 一 下 费 用 流 了 直接就可以考虑一下费用流了
对 每 个 点 进 行 裂 点 , 并 且 由 于 可 能 走 q 次 对每个点进行裂点,并且由于可能走q次 q
所 有 对 每 个 点 裂 点 后 建 q 个 边 , 每 个 边 的 流 量 为 1 , 费 用 变 化 的 点 权 所有对每个点裂点后建q个边,每个边的流量为1,费用变化的点权 q1
然 后 把 每 条 路 进 行 相 连 , 费 用 为 0 , 流 量 i n f 然后把每条路进行相连,费用为0,流量inf 0inf
然 后 把 x i 连 接 到 超 级 源 点 , y i 连 接 到 超 级 汇 点 费 用 为 0 , 流 量 为 1 然后把x_i连接到超级源点,y_i连接到超级汇点费用为0,流量为1 xiyi01
最 后 跑 出 费 用 流 即 可 最后跑出费用流即可
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
#define SZ(x) (int)x.size()
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double,double> pdd;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[][2]={{0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1}};

//spfa
int n,m,s,t,N;
struct edge{
	ll v,nx,f,w;
}e[1000010];
int cnt,maxflow,maxcost;
ll head[maxn],dis[maxn],pre[maxn],maxf[maxn];
bool inq[maxn];
void add(int u,int v,int f,int w){
	e[cnt]={v,head[u],f,w};
	head[u]=cnt++;
	e[cnt]={u,head[v],0,-w};
	head[v]=cnt++;
}
void init(){
	for(int i=0;i<=N;i++)
		head[i]=-1;
	maxflow=maxcost=cnt=0;
}
bool spfa(){
	for(int i=0;i<=N;i++)dis[i]=inf;
	queue<int> q;
	q.push(s);
	dis[s]=0,inq[s]=1,maxf[s]=inf;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		inq[u]=0;
		for(int i=head[u];i!=-1;i=e[i].nx){
			int v=e[i].v;
			if(e[i].f&&dis[v]>dis[u]+e[i].w){
				dis[v]=dis[u]+e[i].w;
				pre[v]=i;
				maxf[v]=min(maxf[u],e[i].f);
				if(!inq[v])inq[v]=1,q.push(v);
			}
		}
	}
	return dis[t]<inf;
}
void MCMF(){
	while(spfa()){
		int u=t,i;
		while(u!=s){
			i=pre[u];
			e[i].f-=maxf[t];
			e[i^1].f+=maxf[t];
			u=e[i^1].v;
		}
		maxflow+=maxf[t];
		maxcost+=dis[t]*maxf[t];
	}
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n,m,q;
    cin>>n>>m>>q;
    N=t=2*n+1;
    init();
    for(int i=1;i<=n;i++){
        ll a,b;
        cin>>a>>b;
        for(int j=0;j<q;j++)
            add(i,i+n,1,a+j*b);
    }
    for(int i=1;i<=m;i++){
        int u,v;
        cin>>u>>v;
        add(u+n,v,inf,0);
        add(v+n,u,inf,0);
    }
    for(int i=1;i<=q;i++){
        int x;cin>>x;
        add(s,x,1,0);
    }
    for(int i=1;i<=q;i++){
        int y;cin>>y;
        add(y+n,t,1,0);
    }
    MCMF();
    cout<<maxcost;
    return 0;
}


你可能感兴趣的:(牛客练习赛65题解)