2021年牛客暑期多校训练营2
2021年牛客暑期多校训练营2
A Arithmetic Progression(待)
题意:
题解:
注意:
代码:
B Cannon(待)
题意:
题解:
注意:
代码:
C Draw Grids
题意:
签到题,轮流进行操作,使得这个图始终不连通
题解:
根据点的奇偶性,进行判断
注意:
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
if((n*m)&1)printf("NO\n");
else printf("YES\n");
return 0;
}
D Er Ba Game
题意:
继续签到,根据题目所说的一个个条件模拟即可
题解:
模拟
注意:
条件考虑的全面充分一点,不要漏了特殊情况,比如说两个数都相等
代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
int t;
scanf("%d", &t);
int a1, b1, a2, b2;
while (t--) {
scanf("%d%d%d%d", &a1, &b1, &a2, &b2);
if (a1 > b1)swap(a1, b1);
if (a2 > b2)swap(a2, b2);
if (a1 == 2 && b1 == 8 && a2 == 2 && b2 == 8) {
printf("tie\n");
} else if (a1 == 2 && b1 == 8) {
printf("first\n");
} else if (a2 == 2 && b2 == 8) {
printf("second\n");
} else if (a1 == a2 && b1 == b2)printf("tie\n");
else if (a1 == b1 && a2 == b2) {
if (a1 > a2)printf("first\n");
else if (a1 < a2)printf("second\n");
else printf("tie\n");
} else if (a1 == b1 && a2 != b2) {
printf("first\n");
} else if (a2 == b2 && a1 != b1) {
printf("second\n");
} else if (a1 != b1 && a2 != b2) {
if ((a1 + b1) % 10 > (a2 + b2) % 10)printf("first\n");
else if ((a1 + b1) % 10 < (a2 + b2) % 10)printf("second\n");
else {
if (b1 > b2)printf("first\n");
else if (b2 > b1)printf("second\n");
}
}
}
return 0;
}
E Gas Station(待)
题意:
题解:
注意:
代码:
F Girlfriend
题意:
给定4个点和2个模式的公式,求这模型形成的空间图形的体积交⛵
题解:
详情参考,通过给定的方程可以发现,在二维中AP=λBP(λ≠1)P点的轨迹是一个圆,那么在三维中P点轨迹则应该是一个球,也就是说是求两个球的体积交。化简公式有
球的方程: 圆心: (-a,-b,-c) 半径:
从而可以找到这两个球的球心和半径,根据球心距离与半径的比较,来判断是否相交😋,接下来就是求相交的体积了,可以用三重积分来求,。这里的h是两球相交的弧线与半径相交的那个点到同一根半径线末端点形成的线段长度,这个长度需要我们自己利用三角函数知识求出来。接着再把两个球的积分体积给加起来就可以了。
这里有一个两圆相交求面积,主要就是合理的利用三角函数来求一些角度从而将面积表示出来,可以参考一下。
注意:
double 类型注意一下
代码:
#include<bits/stdc++.h>
using namespace std;
const double pi=acos(-1);
double x[10],y[10],z[10];
void cal(double *a,double *b,double *c,int k1,int k2)//求球的半径
{
double kt1=k1*k1, ks1=1-kt1;
double x=-(kt1*a[2]-a[1])/ks1;
double y=-(kt1*b[2]-b[1])/ks1;
double z=-(kt1*c[2]-c[1])/ks1;//圆心的位置
double D=(a[1]*a[1]+b[1]*b[1]+c[1]*c[1]-kt1*(a[2]*a[2]+b[2]*b[2]+c[2]*c[2]))/ks1;
double R=sqrt(fabs(x*x+y*y+z*z-D));
double kt2=k2*k2,ks2=1-kt2;
double q=-(kt2*a[4]-a[3])/ks2;
double s=-(kt2*b[4]-b[3])/ks2;
double t=-(kt2*c[4]-c[3])/ks2;
double d=(a[3]*a[3]+b[3]*b[3]+c[3]*c[3]-kt2*(a[4]*a[4]+b[4]*b[4]+c[4]*c[4]))/ks2;
double r=sqrt(q*q+s*s+t*t-d);
double dis=sqrt((x-q)*(x-q)+(y-s)*(y-s)+(z-t)*(z-t));
if(dis>=R+r){
printf("0.000\n");
}
else if(dis+r<=R)printf("%.3lf\n",4*pi*r*r*r/3);
else if(dis+R<=r)printf("%.3lf\n",4*pi*R*R*R/3);
else {
double h=r*(1-(r*r+dis*dis-R*R)/(2.0*r*dis));
double H=R*(1-(R*R+dis*dis-r*r)/(2.0*R*dis));
printf("%.3lf\n",pi*H*H*(R-H/3)+pi*h*h*(r-h/3));
}
}
int main(){
int t,k1,k2;
scanf("%d",&t);
while(t--)
{
for(int i=1;i<=4;i++){
scanf("%lf%lf%lf",&x[i],&y[i],&z[i]);
}
scanf("%d%d",&k1,&k2);
cal(x,y,z,k1,k2);
}
return 0;
}
G League of Legends
题意:
将n个人按照空余时间分成K组,使得这K个组的总时间最大。
题解:
详情参考用到了DP,还有单调队列来维护。这种题只能多写了
注意:
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e3 + 5;
#define chushi(a, b) memset(a, b, sizeof(a))
ll dp[maxn][maxn], num[maxn], q[maxn];
struct node {
ll first, second;
} a[maxn];
bool cmp(node a, node b) {
return a.first == b.first ? a.second < b.second : a.first < b.first;
}
int main() {
int n, k;
scanf("%d%d", &n, &k);
for(int i=1;i<=n;i++)scanf("%lld%lld",&a[i].first,&a[i].second);
sort(a+1, a+1+n, cmp);
ll maxm=2e9;int cnt=0;
for(int i=n;i>=1;i--){
if(a[i].second>=maxm){
num[++cnt]=a[i].second-a[i].first;
a[i].first=2e9;
}
else maxm=a[i].second;
}
sort(a+1, a+1+n, cmp);
chushi(dp, 0xc0);
dp[0][0] = 0;
n -= cnt;
for(int i = 1; i <= min(n, k); i++){
int l,r;l = r = 0; q[++r] = 0; // 维护一个递减队列
for(int j = 1; i <= n; j++){
while(l<r && a[q[l+1]+1].second <= a[j].first) ++l; // 没有相交,出队
if(l < r) dp[i][j] = dp[i-1][q[l+1]] + a[q[l+1]+1].second - a[j].first; //有相交,在队列中找重合度最高的进行分组
if(j == n) break;
while(l<r && dp[i-1][j] + a[j+1].second >= dp[i-1][q[r]] + a[q[r]+1].second) --r;//为下一个dp[i][j]找到最大的值
q[++r] = j;
}
}
sort(num + 1, num + 1 + cnt);
reverse(num + 1, num + 1 + cnt);
for (int i = 2; i <= cnt; i++) {//倒着的前缀和
num[i] += num[i - 1];
}
ll ans = 0;
for (int i = 1; i <= min(n, k); i++) {
ans = max(ans, dp[i][n] + num[max(k - i, 0)]);//表示有前n个人分成了i组,剩下的k-i组由这些包含情况单独成组进行补足
}
printf("%lld\n", ans);
return 0;
}
H Olefin(待)
题意:
题解:
注意:
代码:
I Penguins
题意:
类似于走迷宫,增加了镜像的条件,同时进行广搜就可以了
题解:
详情参考经典广搜题
注意:
特别注意,起点和终点也要标记输出。想明白镜像怎么搞就问题不大了
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e7;
string a[21],b[21];
char dir[5]={'D','L','R','U'};
int x[5]={1,0,0,-1};
int y[5]={0,-1,1,0};
struct node{
int x1,y1,x2,y2;
string path;
};
bool vis[21][21][21][21]={false};
bool pal(int x,int y,string a[])//遇到障碍物,或者出界
{
if(x<0||y>19||y<0||x>19||a[x][y]=='#')return 0;
else return 1;
}
string bfs(){
queue<node>q;
q.push({19,19,19,19,""});
vis[19][19][19][19]=1;
while(!q.empty())
{
node now=q.front();
q.pop();
if(now.x1==0&&now.y1==19&&now.x2==0&&now.y2==19)return now.path;
for(int i=0;i<4;i++){
int x1=now.x1+x[i],x2=now.x2+x[i],y1=now.y1+y[i],y2=now.y2+y[i];//注意镜像
int p1=pal(x1,y1,a),p2=pal(x2,y2,b);
if(p1&&p2){
if(vis[x1][y1][x2][y2])continue;
vis[x1][y1][x2][y2]=1;
q.push({x1,y1,x2,y2,now.path+dir[i]});
}
else if(p1){
if(vis[x1][y1][now.x2][now.y2])continue;
vis[x1][y1][now.x2][now.y2]=1;
q.push({x1,y1,now.x2,now.y2,now.path+dir[i]});
}
else if(p2){
if(vis[now.x1][now.y1][x2][y2])continue;
vis[now.x1][now.y1][x2][y2]=1;
q.push({now.x1,now.y1,x2,y2,now.path+dir[i]});
}
}
}
return "";
}
bool sol(int x,int y,char a,string s[])
{
if(a=='D')x++;
else if(a=='U')x--;
else if(a=='L')y--;
else y++;
if(x<0||x>19||y<0||y>19||s[x][y]=='#')return 0;//走不通
return 1;
}
int main()
{
for(int i=0;i<20;i++){
cin>>a[i]>>b[i];
reverse(b[i].begin(),b[i].end());
}
string ans=bfs();//得到一条路径
int x1=19,y1=19,x2=19,y2=19;
for(int i=0;i<ans.size();i++){
if(sol(x1,y1,ans[i],a)){
if(ans[i]=='U')x1--;
else if(ans[i]=='D')x1++;
else if(ans[i]=='L')y1--;
else y1++;
a[x1][y1]='A';//表示企鹅当前的位置
}
if(sol(x2,y2,ans[i],b)){
if(ans[i]=='U')x2--;
else if(ans[i]=='D')x2++;
else if(ans[i]=='L')y2--;
else y2++;
b[x2][y2]='A';//表示企鹅当前的位置
}
}
a[19][19]=b[19][19]=a[0][19]=b[0][19]='A';//起点和终点
cout<<ans.size()<<endl;
cout<<ans<<endl;
for(int i=0;i<20;i++){
reverse(b[i].begin(),b[i].end());
cout<<a[i]<<" "<<b[i]<<endl;
}
return 0;
}
J Product of GCDs
题意:
给定一个集合,求子集大小为k的gcd()的连乘
题解:
简单来说就是从n个里面选取k个数,然后求他们的连乘,我们这里通过将所有的质因数打表出来,他们的gcd值一定是为质因数的乘积,现如今我将这个gcd值分解出来,分成许多个质因数,通过质因数遍历去找是否存在k个这样的值,存在则乘pow(primer[i],c[num,k]),因为乘起来也构成了gcd的次幂。最重要的是这个欧拉降幂
注意:
signed main()需要用long long格式,还有就是const比#define快,尽量用const,我就被卡这了
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e6+5;
const int N=1e7+5;
const int INF=0x3f3f3f3f;
bool vis[N];
int a[maxn],prime[maxn],num[maxn],c[40005][35];
int cnt=0;
int ksm(int a,int b,int mod)
{
__int128 ans=1,base=a;
while(b)
{
if(b&1)ans=(ans*base)%mod;
base=(base*base)%mod;
b>>=1;
}
return ans%mod;
}
void eul(int n){
for(int i=2;i<=n;i++){
if(!vis[i])prime[++cnt]=i;
for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0)break;//已经找到了i归属的质数,不需要继续遍历下去了
}
}
}
int phi(int n)
{
int res=n;
for(int i=1;prime[i]*prime[i]<=n;i++){//没有超出范围的质数
if(n%prime[i]==0){
res=res/prime[i]*(prime[i]-1);
while(n%prime[i]==0)n/=prime[i];
}
}
if(n>1)res=res/n*(n-1);
return res;
}//欧拉函数
signed main()
{
ios::sync_with_stdio(false),cin.tie(0);cout.tie(0);
eul(1e7);
int t,n,k,p;scanf("%lld",&t);
while(t--)
{
scanf("%lld%lld%lld",&n,&k,&p);
int maxm=-INF;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
num[a[i]]++;
maxm=max(maxm,a[i]);
}
int mod=phi(p);
c[0][0]=1;
for(int i=1;i<=n;i++){
c[i][0]=1;
for(int j=1;j<=min(i,k);j++){
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;//打表组合数,欧拉降幂
}
}
int ans=1;
for(int i=1;prime[i]<=maxm;i++){
for(int j=prime[i];j<=maxm;j*=prime[i])
{
int tmp=0;
for(int K=j;K<=maxm;K+=j)tmp+=num[K];//满足条件的里面选取k个数
if(tmp<k)break;
ans=(__int128)ans*ksm(prime[i],c[tmp][k],p)%p;
}
}
for(int i=1;i<=n;i++)num[a[i]]=0;
cout<<ans<<endl;
}
return 0;
}
K Stack
题意:
模拟的一个单调栈过程,根据k个位置所对应的当前的单调栈的大小,去推理出1-n,这个序列的排序方式。并输出该序列
题解:
通过构造出一个表示单调栈大小的完整序列,用这个序列,进行拓扑排序的形式表示出原有的序列。注意构建方式,如果某个值相同的话,序号大的数优先赋值。详情参考,图解非常的详细。
注意:
特别要注意的就是给定的k个数之间构建完了之后,还要考虑首端和尾端的情况,这里构建了一个inde[k+1]=n+1,num[n+1]=0;就是为了更好的处理末尾。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int num[maxn],inde[maxn],xt[maxn];
int main()
{
int n,k,s,ans,sum,t,now,x;
bool flag=true;
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++){
scanf("%d%d",&inde[i],&x);
num[inde[i]]=x;
if(x>inde[i])flag=false;
}
if(!flag){
printf("-1\n");
return 0;
}
sort(inde+1,inde+k+1);
inde[k+1]=n+1;
num[n+1]=0;
for(int i=1;i<=k;i++){
if(num[inde[i+1]]-num[inde[i]]>inde[i+1]-inde[i]){
printf("-1\n");
return 0;
}
if(num[inde[i]]>=num[inde[i+1]]) {
t = inde[i] + 1;
while (t < inde[i + 1])num[t++] = num[inde[i]];
}
else {
for (int j = inde[i + 1] - 1; j > inde[i]; j--) {
num[j] = num[j + 1] - 1;
num[j] = max(num[j], num[inde[i]]+1);
}
}
}
for(int i=inde[1]-1;i>=1;i--){
num[i]=num[i+1]-1;
num[i]=max(num[i],1);
}
ans=0,now=1;
/*for(int i=1;i<=n;i++){
printf("%d ",num[i]);
}*/
for(int i=1;i<=n;i++){
ans=max(ans,num[i]);
}
vector<int>v[ans+1];
for(int i=n;i>=1;i--){
v[num[i]].push_back(i);
}
for(int i=1;i<=ans;i++){
for(int j=0;j<v[i].size();j++){
xt[v[i][j]]=now++;
}
}
for(int i=1;i<=n;i++){
printf("%d ",xt[i]);
}
return 0;
}
L WeChat Walk(待)
题意:
有一个无向图来描述朋友圈,一天分成有q个时间段,每个时间段只有一个人走,给定走的人和走的步数。计算出每个人在自己的朋友圈里边成为冠军的次数
题解:
注意:
代码:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具