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

相關文章
相關標籤/搜索