2021牛客暑期多校训练营2

2021牛客暑期多校训练营2

A Arithmetic Progression

题目大意
给定一个序列\(a_i\),求全部子区间,知足排序后是等差数列的个数
分析
首先须要一个快速的断定方法:
对于序列\(b_i\),若其排序后为等差数列,则必然有公差\(d=gcd(b_2−b_1,b_3−b_2,...)\) ,证实略去
因而问题转化为统计区间(l,r)知足:
$ max(a[l..r])−min(a[l..r])=(r−l) ∙|gcd(...)| \( 枚举r,能够在结合单调栈在线段树上维护max−min的值,处理区间修改操做 对于不一样的\)l,gcd\(的不一样分段只有\)log a$ 段,能够在l每次gcd 改变时在线段树上作单点修改
又$max(l,r)−min(l,r)−(r−l) ∙|gcd(...)|≥0 $,故能够统计线段树上的最小值及其出现的次数
就能获得区间内该表达式为0的个数node

B Cannon

C Draw Grids

题目大意
给定一个n×m的点阵,每次选两个相邻点连线
两我的轮流操做,不能连出封闭图形,不能操做者输
分析
不能连出封闭图形就是不能造成环,也就是图始终是一片森林
终态必定是一棵生成树,所以根据点数奇偶性便可判断ios

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int read(){
	int sum=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
	return sum*f;
}
int main(){
	int n=read(),m=read();
	if(n>m)swap(n,m);
	if(n==1){
		if(m&1==1)printf("NO");
		else printf("YES"); 
	}
	if(n==2){
		printf("YES");
	}
	if(n==3){
		if(m==3)printf("NO");
        else printf("YES");
	}
	if(n==4){
		printf("YES");
	}
	return 0;
}

D Er Ba Game

签到模拟题c++

#include <bits/stdc++.h>
#define ll long long
#define int ll 
#define mod 100000000
#define N 100010
#define long_long_MAX 9187201950435737471
#define long_long_MIN -9187201950435737472
#define int_MAX 2139062143
#define int_MIN -2139062144
#define jh(x, y) (x ^= y ^= x ^= y)
#define loc(x, y) ((x - 1) * m + y)
#define lowbit(x) (x & -x)
using namespace std;

ll max(ll x,ll y){return x > y ? x : y;}

ll min(ll x,ll y){return x > y ? y : x;}

inline ll read()
{
    ll a=0;ll f=0;char p=getchar();
	while(!isdigit(p)){f|=p=='-';p=getchar();}
	while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=getchar();}
	return f?-a:a;
}

inline void print(ll x)
{
    if(!x) return;
    if(x) print(x/10);
    putchar(x%10+'0');
}

int t;

signed main()
{
    t = read();
    while(t--)
    {
        int a1 = read(),b1 = read(),a2 = read(),b2 = read();
        if(a1 > b1) swap(a1,b1);
        if(a2 > b2) swap(a2,b2);
        if((a1 == 2 && b1 == 8) || (a2 == 2 && b2 == 8))
        {
            if(a1 == 2 && b1 == 8 && a2 == 2 && b2 == 8) printf("tie\n");
            else if(a1 == 2 && b1 == 8) printf("first\n");
            else printf("second\n");
        }
        else if((a1 == b1) || (a2 == b2))
        {
            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) printf("first\n");
            else printf("second\n");
        }
        else if(a1 != b1 && a2 != b2)
        {
            int x1 = (a1 + b1) % 10;
            int x2 = (a2 + b2) % 10;
            if(x1 > x2) printf("first\n");
            else if(x1 < x2) printf("second\n");
            else
            {
                if(b1 > b2) printf("first\n");
                else if(b1 < b2) printf("second\n");
                else printf("tie\n");
            }
        }
        else printf("tie\n");
    }
    system("pause");
    return 0;
}

E Gas Station

题目大意
给定一棵树,每次从 s 出发,初始权值是 x ,过一条边消耗 w ,到一个点加$ a_i$
要求过程权值非负,不容许通过一个点 p ,求可达点个数
分析
离线进行点分治,每次求跨过点分治的根的可达点数目
能够预处理每一个点能不能到根,从根下去到这个须要多少初始权值
讨论p 所在位置,减去跨过p或本身子树内的部分
求子树内的答案能够先对于预处理的权值离散,
而后树状数组 + dfs 做差 进行维护
复杂度为 \(O(nlog^2n)\) ,常数不算太大
( 复古点分治题。。。git

F Girlfriend

题目大意
空间内有6个点,知足$|P_1A|≥k_1|P_1B|,|P_2C|≥k_2|P_2D| \( 求\)P_1\(,\)P_2$ 各自轨迹围成的空间体的体积交
分析
对于固定的\(k\),\(P_1\),\(P_2\) 的轨迹构成标准 阿波罗尼斯 球的球壳
对于\(≥k\) ,便是实心的球体,因而问题转化为求球的体积交
这个模板满世界都是,本身推的话,能够直接对于交部分的截面作面积的积分
大概是 \(∫π(r^2−x^2) dx\) 的形式,比较简单数组

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const double pi=acos(-1);
const int MAX=100+10;
const int inf=1e9+7;
typedef struct{
	double x,y,z,r;
}Point;
int n;
Point a[MAX];
Point s,b,c,d,e;
double dis(Point p,Point q){
	double ans=sqrt((p.x-q.x)*(p.x-q.x)+(p.y-q.y)*(p.y-q.y)+(p.z-q.z)*(p.z-q.z));
	return ans;
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		scanf("%lf%lf%lf", &b.x, &b.y, &b.z);
		scanf("%lf%lf%lf", &c.x, &c.y, &c.z);
		scanf("%lf%lf%lf", &d.x, &d.y, &d.z);
		scanf("%lf%lf%lf", &e.x, &e.y, &e.z);
		int k1,k2;
		scanf("%d%d",&k1,&k2);
		double r1=dis(b,c)/(k1+1)/(k1-1)*k1;
		double r2=dis(d,e)/(k2+1)/(k2-1)*k2;
		double ans=r1*r1*r1*pi*4/3+r2*r2*r2*pi*4/3;
		
		a[0].x=((k1*c.x+b.x)/(k1+1)+(k1*c.x-b.x)/(k1-1))/2;
		a[0].y=((k1*c.y+b.y)/(k1+1)+(k1*c.y-b.y)/(k1-1))/2;
		a[0].z=((k1*c.z+b.z)/(k1+1)+(k1*c.z-b.z)/(k1-1))/2;
		a[0].r=r1;
		
		s.x=((k2*e.x+d.x)/(k2+1)+(k2*e.x-d.x)/(k2-1))/2;
		s.y=((k2*e.y+d.y)/(k2+1)+(k2*e.y-d.y)/(k2-1))/2;
		s.z=((k2*e.z+d.z)/(k2+1)+(k2*e.z-d.z)/(k2-1))/2;
		s.r=r2;
		
		double d=dis(s,a[0]);
		if(s.r<a[0].r){
			swap(s.r,a[0].r);
			swap(s.x,a[0].x);
			swap(s.y,a[0].y);
			swap(s.z,a[0].z);
		}
		if(a[0].x==s.x&&a[0].y==s.y&&a[0].z==s.z){
			printf("%.3lf\n",ans-s.r*s.r*s.r*4.0*pi/3.0);
			continue;
		}
		double tmp=0;
		if(d>=s.r+a[0].r){
			printf("%.3lf\n",ans-(a[0].r*a[0].r*a[0].r+s.r*s.r*s.r)*4.0*pi/3.0);
			continue;
		}
		else if(d+a[0].r<=s.r){
			tmp+=(4.0/3.0)*pi*a[0].r*a[0].r*a[0].r;
		}
		else {
        	double co=(s.r*s.r+d*d-a[0].r*a[0].r)/(2.0*d*s.r);
        	double h=s.r*(1.0-co);
        	tmp+=(1.0/3.0)*pi*(3.0*s.r-h)*h*h;
        	co=(a[0].r*a[0].r+d*d-s.r*s.r)/(2.0*d*a[0].r);
       		h=a[0].r*(1.0-co);
       		tmp+=(1.0/3.0)*pi*(3.0*a[0].r-h)*h*h;
    	}
    	printf("%.3lf\n",ans-((a[0].r*a[0].r*a[0].r+s.r*s.r*s.r)*4.0*pi/3.0-tmp));
	} 
	return 0;
}

G League of Legends

题目大意
给定n个区间,要求将它们分红k组,每组之间有交,最大化每组交长度之和
分析
首先考虑简化问题,对于每个包含其它区间的大区间,其最优决策有两种
1.归属到一个被它包含的区间所在的组,不影响答案
2.独自一组,长度直接算入答案
剩下的部分均是独立的小区间,不妨提取出来为$(a_i,b_i) \(,排序以后进行dp 令\)dp_{i,j}$表示前i个,分了j组的方案数,转移为
$dp_{i,j} + b_i− a_k→ dp_{k,j}+1 \(,且须要知足\)b_i> a_k \(,容易用单调队列优化 求得\)dp_{n’,i} \(后再综合大区间的贡献便可 复杂度为\)O(n^2) \(,常数较小。应该故意放过了带\)log $的作法。ide

H Olefin

I Penguins

广搜模拟题优化

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
char a[22][22],b[22][22];
int x[4]={1,0,0,-1};
int y[4]={0,-1,1,0};
char step[4]={'D','L','R','U'};
struct node{
	int ax,ay,bx,by,step;
	node (int AX,int AY,int BX,int BY,int S){
		ax=AX,ay=AY,bx=BX,by=BY,step=S;
	}
};
string S[22][22][22][22];
bool vis[22][22][22][22];
queue<node> q;
void bfs(){
	q.push(node(20,20,20,1,0));
	vis[20][20][20][1]=1;
	while(!q.empty()){
		node now=q.front();
		if(now.ax==1&&now.ay==20&&now.bx==1&&now.by==1)break;
		q.pop();
		for(int i=0;i<=3;i++){
			int AX=now.ax+x[i],AY=now.ay+y[i],BX=now.bx+x[i],BY=now.by-y[i];
			if(a[AX][AY]!='.'&&b[BX][BY]!='.')continue;
			if(b[BX][BY]=='.'&&a[AX][AY]=='.'){
				if(vis[AX][AY][BX][BY]==1)continue;
				q.push(node(AX,AY,BX,BY,now.step+1));
				vis[AX][AY][BX][BY]=1; 
				S[AX][AY][BX][BY]=S[now.ax][now.ay][now.bx][now.by];
				if(i==0)S[AX][AY][BX][BY]+="D";
				if(i==1)S[AX][AY][BX][BY]+="L";
				if(i==2)S[AX][AY][BX][BY]+="R";
				if(i==3)S[AX][AY][BX][BY]+="U";
			}
			else if(a[AX][AY]!='.'){
				if(vis[now.ax][now.ay][BX][BY]==1)continue;
				q.push(node(now.ax,now.ay,BX,BY,now.step+1));
				vis[now.ax][now.ay][BX][BY]=1;
				S[now.ax][now.ay][BX][BY]=S[now.ax][now.ay][now.bx][now.by];
				if(i==0)S[now.ax][now.ay][BX][BY]+="D";
				if(i==1)S[now.ax][now.ay][BX][BY]+="L";
				if(i==2)S[now.ax][now.ay][BX][BY]+="R";
				if(i==3)S[now.ax][now.ay][BX][BY]+="U";
			}
			else{
				if(vis[AX][AY][now.bx][now.by]==1)continue;
				q.push(node(AX,AY,now.bx,now.by,now.step+1));
				vis[AX][AY][now.bx][now.by]=1;
				S[AX][AY][now.bx][now.by]=S[now.ax][now.ay][now.bx][now.by];
				if(i==0)S[AX][AY][now.bx][now.by]+="D";
				if(i==1)S[AX][AY][now.bx][now.by]+="L";
				if(i==2)S[AX][AY][now.bx][now.by]+="R";
				if(i==3)S[AX][AY][now.bx][now.by]+="U";
			}
		}
	}
}
int main(){
    //freopen("i.in","r",stdin);
    //freopen("i.out","w",stdout);
	for(int i=1;i<=20;i++){
		for(int j=1;j<=20;j++)
			a[i][j]=getchar();
		getchar();
		for(int j=1;j<=20;j++)
			b[i][j]=getchar();
		getchar();
	}
	bfs();
	cout<<q.front().step<<endl;
	int len=S[1][20][1][1].size();
	int ax=20,ay=20,bx=20,by=1;
	a[ax][ay]='A';b[bx][by]='A'; 
	cout<<S[1][20][1][1];
	cout<<endl;
	for(int i=0;i<len;i++){
		char now=S[1][20][1][1][i];
		int step;
		if(now=='D')step=0;
		if(now=='L')step=1;
		if(now=='R')step=2;
		if(now=='U')step=3;
		if(a[ax+x[step]][ay+y[step]]=='.' || a[ax+x[step]][ay+y[step]]=='A')ax=ax+x[step],ay=ay+y[step];
		if(b[bx+x[step]][by-y[step]]=='.' || b[bx+x[step]][by-y[step]]=='A')bx=bx+x[step],by=by-y[step];
		a[ax][ay]=b[bx][by]='A';
	}
	for(int i=1;i<=20;i++){
		for(int j=1;j<=20;j++)
			cout<<a[i][j];
		cout<<" ";
		for(int j=1;j<=20;j++)
			cout<<b[i][j];
		cout<<endl; 
	}
    //system("pause");
	return 0;
}

J Product of GCDs

题目大意
给定一个集合,求其全部大小为k的子集的gcd之积
分析
考虑分质因子统计,计算\(f_{p,c}\)表示至少包含$p^c \(的数个数,方案数为\)(\frac{f_{p,c}}k)\(,直接对于差分累和便可 注意须要计算获得的其实是指数,所以咱们须要对于\)φ(P)\(取模 经过分解质因数暴力求φ(P)便可,只须要预处理\)√P \(内的质数,复杂度为\)O(√P+T\frac{√P}{log P})$
\(φ(P)\)不是质数,可是鉴于k较小,组合数直接\(O(nk)\)递推便可(为此 n 开得比较小)
容斥须要 \(O(xlog x)\)的时间,可是没有乘法,常数不大
最后统计答案还须要\(O(\frac{x}{log x})\)次快速幂,模乘法须要快速乘,可是有int128
最后统计答案部分复杂度为\(O(x)\)
统计每一个数倍数的个数,若是对于每一个读入的\(x\)分解,复杂度为\(|S|√x\),理应不能经过
用ln级别的累和则能够,并且实际上只须要对于每一个质因子幂次进行计算
(其实我不会算复杂度,反正不是满的ln)
std在使用上述的暴力分解P的状况下依然表现良好 (500ms ~)
可是写很差可能依然有点卡常,若是卡的话建议直接换\(Pollard’s Rho\)质因数分解,说不定快不少ui

K Stack

题目大意
已知若干时刻的单调栈大小,构造一个合法的序列
分析
对于没有给定b的位置,必定往直接单调栈插入元素
不然根据给定的单调栈大小判断弹掉栈顶的几个元素,若是不够就无解
每次根据被弹掉的最后一个元素,以及没有弹掉的栈顶元素
由此,构造出一组拓扑关系
而后直接作一次拓扑排序便可构造出一组合法解
在选手代码中也看到了直接用list 维护的spa

#include<bits/stdc++.h>
using namespace std;
const int M=1e6+5;

void Rd(int &r)
{
	static char c;
	for(c=0;c<'0' || c>'9';c=getchar());
	for(r=0;c>='0' && c<='9';c=getchar())r=r*10+(c^'0');
}

int n,m;
int Q[M];

int L[M],R[M],I[M];void Link(int l,int r){L[r]=l,R[l]=r;}
int Stk[M];

void Solve()
{
	Link(0,n+1);
	int pos=1,sv=0;
	for(int i=1;i<=n;i++)
	{
		if(Q[i]==-327327327)continue;
		if(Q[i]<1 || Q[i]>i){puts("-1");return;}
		if(Q[i]-sv > i-pos+1){puts("-1");return;}
		while(pos<=i)
		{
			if(sv<Q[i])
			{
				Link(pos,R[Stk[sv]]),Link(Stk[sv],pos);
				sv++,Stk[sv]=pos;
			}
			else
			{
				sv=Q[i]-1;
				Link(pos,R[Stk[sv]]),Link(Stk[sv],pos);
				Stk[++sv]=pos;
			}
			pos++;
		}
	}
	while(pos<=n)Link(pos,R[Stk[sv]]),Link(Stk[sv],pos),pos++;
	for(int i=R[0],cnt=1;i<=n;i=R[i],cnt++)I[i]=cnt;
	for(int i=1;i<=n;i++)printf("%d ",I[i]);
}

int main()
{
	Rd(n);Rd(m);if(m>n)return 0;
	for(int i=1;i<=n;i++)Q[i]=-327327327;
	for(int i=1,x;i<=m;i++)
	{
		Rd(x),Rd(Q[x]);
		if(x<1 || x>n || Q[x]<1 || Q[x]>n)return 0;
	}
	Solve();
	system("pause");
	return 0;
}

L WeChat Walk

题目大意
给定n我的之间的好友关系,每次单点增长一我的的步数
求每一个人在本身列表里保持冠军的时间
分析
设界值 S=√n ,权值值域W=10000
按照S分块考虑点,称度数≤S的点为小点,度数>S的点为大点
依次考虑每个时刻的事件,维护每一个人成为冠军的区间
假设被修改的点为x ,容易分红若干状况
1.若是原先x是冠军,无需修改冠军状况
2.x不是冠军,设原先权值为a,新权值为b,a<b
2-1.x是小点
直接枚举更新全部和x相邻的点,更新它们的冠军状况,同时也就知道了x是否成为了冠军
2-2.x是大点
2-2-1. 大点周围的大点
直接枚举而后更新便可
2-2-2.大点周围的小点
注意到W较小,所以能够直接暴力存下和x相邻的 小点 中,权值为w的且为冠军的点的集合\(C_x\),w
成为冠军的时间只有在每次小点被增长才会出现,C的总元素个数为O(nS)
暴力扫描权值,判断i∈(a,b],\(C_x\),i 中的点是否再也不成为冠军 ,容易发现无需维护C的删除操做
元素和集合不会被重复扫描, 所以复杂度为O(SW+nS)
容易发现W无限制时也彻底可写,可是据出题人称这样很是方便code

相关文章
相关标签/搜索