其他分享
首页 > 其他分享> > "蔚来杯"2022牛客暑期多校训练营2

"蔚来杯"2022牛客暑期多校训练营2

作者:互联网

链接

\(C:Link with Nim Game\)

判断必胜态还是必败态很明显直接求异或和就好了。
异或和为 \(0\) ,则为必败态,我们肯定是希望尽量一次每人取一个石子。
存在这种方案吗?我们可以找到 \(lowbit\) 最小的那堆石子取一个,这样对方肯定也只能从 \(lowbit\) 相等的那堆石子中取一个。
存在几种这样的方案?我们设一堆石子为 \(x\) ,我们从中取一个后,异或值变为 \(2 \cdot lowbit(x)-1\)。
有哪些数目的石子可以取超过一个是异或值再变为零?设这堆石子有 \(y\) 个,那么显然 \(y \& lowbit(x)=lowbit(x)\),且 \(y\) 的更低位不全为 \(0\),因为我们可以把更低位的那些 \(1\) 取了,再取 \(1\) 个石子满足条件。
对于先手必胜的情况,找到可以一次取的最大石子数并统计数量即可。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define LF long double
using namespace std;
const int N=1e5+3;
LL n,a[N];
map<LL,LL>mp,MP;
IL LL in(){
    char c;int f=1;
    while((c=getchar())<'0'||c>'9')
      if(c=='-') f=-1;
    LL x=c-'0';
    while((c=getchar())>='0'&&c<='9')
      x=x*10+c-'0';
    return x*f;
}
IL LL lb(LL x){return x&-x;}
IL void solve(){
	mp.clear(),MP.clear();
	LL sum=0,sump=0,ans1=0,ans2=0;
	n=in();
	for(int i=1;i<=n;++i) sump^=(a[i]=in()),sum+=a[i];
	if(!sump){
		for(LL i=0;i<=32;++i){
			int cnt=0,flag=0;
			for(LL j=1;j<=n;++j)
			  if(a[j]&(1ll<<i)){
			  	++cnt;
			  	if(a[j]&((1ll<<i)-1)) flag=1;
			  }
			if(!flag) ans2+=cnt;
		}
		printf("%lld %lld\n",sum,ans2);
	}
	else{
		ans1=0;
		for(int i=1;i<=n;++i){
			LL x=a[i]-(sump^a[i]);
			if(x>0){
				if(ans1<x) ans1=x,ans2=1;
				else if(ans1==x) ++ans2;
			}
		}
		printf("%lld %lld\n",sum-ans1+1,ans2);
	}
}
int main()
{
    int T=in();
    while(T--) solve();
    return 0;
}

\(D:Link with Game Glitch\)

我们可以二分 \(w\),将每条边 \(\frac{c}{a}\) 乘上 \(w\),用 \(spfa\) 判断是否存在大于 \(1\) 的环即可。
实际代码要取对数,变为判断正环。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define LF long double
using namespace std;
const int N=1e3+3;
struct hh{
	int to,nxt;LF w;
}e[N<<1];
int n,m,num,fir[N],vis[N],cnt[N];
LF dis[N];
IL LL in(){
    char c;int f=1;
    while((c=getchar())<'0'||c>'9')
      if(c=='-') f=-1;
    LL x=c-'0';
    while((c=getchar())>='0'&&c<='9')
      x=x*10+c-'0';
    return x*f;
}
IL void add(int x,int y,LF w){
	e[++num]=(hh){y,fir[x],w},fir[x]=num;	
}
queue<int>q;
int pd(LF w){
	while(q.size()) q.pop();
	for(int i=1;i<=n;++i) dis[i]=cnt[i]=0;
	for(int i=1;i<=n;++i) q.push(i),vis[i]=1;
	while(q.size()){
		int u=q.front();q.pop();vis[u]=0;
		for(int i=fir[u],v;v=e[i].to;i=e[i].nxt)
		  if(dis[v]<dis[u]+e[i].w+w){
		  	dis[v]=dis[u]+e[i].w+w;
		  	cnt[v]=cnt[u]+1;
		  	if(!vis[v]) q.push(v),vis[v]=1;
		  	if(cnt[v]>n) return 1;
		  }
	}
	return 0;
}
int main()
{
	int a,b,c,d;
	n=in(),m=in();
	for(int i=1;i<=m;++i){
		a=in(),b=in(),c=in(),d=in();
		add(b,d,log2(1.0*c)-log2(1.0*a));
	}
	LF l=1.0/1000,r=1;int t=100;
	while(t--){
		LF mid=(l+r)/2;
		if(pd(log2(mid))) r=mid;
		else l=mid;
	}
	printf("%.15LF\n",(l+r)/2);
    return 0;
}

\(E:Falfa with Substring\)

设 \(f_i=C_{n-2i}^{i} 26^{n-3i}\) ,\(g_i\) 为恰好出现 \(i\) 次 \(bit\) 的方案数。
显然 \(f_i= \sum\limits_{j=i}^{n/3}C_{j}^{i}g_j\),
二项式反演可得 \(g_i=\sum\limits_{j=i}^{n/3}(-1)^{j-i}C_{j}^{i}f_j\)。
变形得 \(g_i \cdot i!=\sum\limits_{j=i}^{n/3}\frac{(-1)^{j-i}}{(j-i)!} \times f_j \cdot j!\)。
\(NTT\) 即可。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=4e6+3,M=1e7+3,p=998244353,G=3,Gi=332748118;
int n,m,S,a[N],b[N],c[N],r[N],val[N],fac[M],inv[M],cf[N],ans;
IL int in(){
	char c;int f=1;
	while((c=getchar())<'0'||c>'9')
	  if(c=='-') f=-1;
	int x=c-'0';
	while((c=getchar())>='0'&&c<='9')
	  x=x*10+c-'0';
	return x*f;
}
IL int mod(int x){return x>=p?x-p:x;}
IL int ksm(int a,int b){
  int c=1;
  while(b){
  	if(b&1) c=1ll*c*a%p;
  	a=1ll*a*a%p,b>>=1;
  }
  return c;
}
IL void calc(int lim){
	for(int i=0;i<lim;++i)
	  r[i]=(r[i>>1]>>1)|((i&1)*(lim>>1));
}
IL void init(int n){
	fac[0]=1;for(int i=1;i<=n;++i) fac[i]=1ll*fac[i-1]*i%p;
	inv[n]=ksm(fac[n],p-2);
	for(int i=n;i;--i) inv[i-1]=1ll*inv[i]*i%p;
	cf[0]=1;for(int i=1;i<=n;++i) cf[i]=1ll*cf[i-1]*26%p;
}
IL void NTT(int *a,int lim,int op){
	calc(lim);
	for(int i=0;i<lim;++i)
	  if(i<r[i]) swap(a[i],a[r[i]]);
	for(int i=1;i<lim;i<<=1){
		int wn=ksm(~op?G:Gi,(p-1)/(i<<1));
		for(int j=0;j<lim;j+=i<<1){
			int w=1;
			for(int k=0;k<i;++k,w=1ll*w*wn%p){
				int x=a[j+k],y=1ll*w*a[j+i+k]%p;
				a[j+k]=mod(x+y),a[j+i+k]=mod(x-y+p);
			}
		}
	}
	if(op==-1){
		int inv=ksm(lim,p-2);
		for(int i=0;i<lim;++i) a[i]=1ll*a[i]*inv%p;
	}
}
IL void mul(int *a,int *b,int *c,int n,int m){
	int lim=1,_a[N],_b[N];
	while(lim<n+m-1) lim<<=1;
	memcpy(_a,a,4*n),memcpy(_b,b,4*m),
	memset(_a+n,0,4*(lim-n)),memset(_b+m,0,4*(lim-m));
	NTT(_a,lim,1),NTT(_b,lim,1);
	for(int i=0;i<lim;++i) c[i]=1ll*_a[i]*_b[i]%p;
	NTT(c,lim,-1);
}
IL int C(int n,int m){return 1ll*fac[n]*inv[m]%p*inv[n-m]%p;}
int main()
{
	n=in(),init(n);
	int Max=n/3;
	for(int i=0;i<=Max;++i)
	  a[i]=1ll*C(n-2*i,i)*cf[n-3*i]%p*fac[i]%p;
	for(int i=0;i<=Max;++i)
	  if(i&1) b[i]=mod(p-inv[i]);
	  else b[i]=inv[i];
	reverse(a,a+Max+1);
	mul(a,b,a,Max+1,Max+1);
	reverse(a,a+Max+1);
    for(int i=0;i<=Max;++i) a[i]=1ll*a[i]*inv[i]%p;
    for(int i=0;i<=Max;++i) printf("%d ",a[i]);
    for(int i=Max+1;i<=n;++i) printf("0 ");
	return 0;
}

\(F:NIO with String Game\)

离线,先把包含所有 《完全体》 \(t\) 的 \(trie\) 建出来,发现按序遍历的欧拉序的相对大小即为字符串的相对大小,我们记录每个 \(t\) 和 \(s\) 的终止节点,答案就是欧拉序小于 \(s\) 所在终止节点的 \(t\) 中节点的个数,用树状数组维护即可。
考虑操作 \(1\) ,我们直接在 \(trie\) 上往下走,并在 \(BIT\) 中修改贡献即可。
考虑操作 \(2\) ,我们维护一个向祖先跳的倍增数组,直接跳即可。
考虑操作 \(3\) ,我们维护从某个节点一直加同一字符最远可到达的节点,直接跳下去,跳多了倍增回来,跳少了就记录一下多出的第一个字符。
考虑操作 \(4\) ,如果没有多出的字符则直接查询即可,如果有,对于多出的第一个字符 \(c\),在 \(c-1\) ~ \(0\) 中按顺序找是否有当前节点的儿子,如果有则查询其子树的 \(dfn\) 最大的节点,否则直接查询当前所在的节点。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const LL N=1e6+3;
string s,t[N];
char cc[N];
struct que{
	LL op,pos;char c;
}fn[N];
LL n,q,len[N],pos[N],now,las;LL Len;
struct BIT{
	LL c[N],n;
	IL void init(LL x){n=x;}
	IL LL lb(LL x){return x&-x;};
	IL void add(LL y,LL x){
		for(;y<=n;y+=lb(y)) c[y]+=x;
	}
	IL LL ask(LL y){
		LL x=0;
		for(;y;y-=lb(y)) x+=c[y];
		return x;
	}
}Bit;
struct Trie{
	LL cnt,num,ch[N][26],fa[N][20],to[N][26],dfn[N],ed[N],dep[N];
	IL void insert(LL k,LL l){
		LL len=t[k].size(),u=1,v;
		for(LL i=0;i<len;++i){
			v=t[k][i]-'a';
			if(!ch[u][v]) ch[u][v]=++cnt;
			u=ch[u][v];
			if(i==l-1) pos[k]=u;
		}
	}
	void dfs(LL u,LL f){
		fa[u][0]=f,dfn[u]=++num,dep[u]=dep[f]+1;
		for(LL i=0;fa[fa[u][i]][i];++i)
		  fa[u][i+1]=fa[fa[u][i]][i];
		for(LL i=0;i<26;++i)
		  if(ch[u][i]) dfs(ch[u][i],u),to[u][i]=to[ch[u][i]][i];
		  else to[u][i]=u;
		ed[u]=num;
	}
	void find(){
		LL u=1,v;
		for(LL i=0;i<Len;++i){
			v=s[i]-'a';
			if(ch[u][v]) u=ch[u][v];
			else{las=v;break;}
		}
		now=u;
	}
	void add(LL k,char c){
		Bit.add(dfn[pos[k]],-1),
		Bit.add(dfn[pos[k]=ch[pos[k]][c-'a']],1);
	}
	IL void jump(LL dis){
		for(LL i=0;dis;dis>>=1,++i)
		  if(dis&1) now=fa[now][i];
	}
	void Add(LL num,char c){
		if(dep[now]==Len){
			now=to[now][c-'a'];Len+=num;
			if(dep[now]<Len) las=c-'a';
			else jump(dep[now]-Len);
		}
		else Len+=num;
	}
	void dele(LL num){
		Len-=num;
		if(dep[now]>Len) jump(dep[now]-Len);
	}
	void ask(){
		if(dep[now]==Len) cout<<Bit.ask(dfn[now]-1)<<endl;
		else{
			for(LL i=las-1;~i;--i)
			  if(ch[now][i]){cout<<Bit.ask(ed[ch[now][i]])<<endl;return;}
			cout<<Bit.ask(dfn[now])<<endl;
		}
	}
}T;
int main()
{
	cin.tie(0)->sync_with_stdio(0);
    cin.exceptions(cin.failbit);
    cin.tie(NULL);
    cout.tie(NULL);
    cin>>n>>q>>s,Len=s.size(),T.cnt=1,T.dep[0]=-1;
    for(LL i=1;i<=n;++i) cin>>t[i],len[i]=t[i].size();
    for(LL i=1;i<=q;++i){
    	cin>>fn[i].op;
    	if(fn[i].op==1){
    		cin>>fn[i].pos>>cc;
    		t[fn[i].pos]+=cc[0];
    		fn[i].c=cc[0];
    	}
    	else if(fn[i].op==2) cin>>fn[i].pos;
    	else if(fn[i].op==3) cin>>fn[i].pos>>cc,fn[i].c=cc[0];
    }
    for(LL i=1;i<=n;++i) T.insert(i,len[i]);
    T.dfs(1,0),Bit.init(T.num),T.find();
    for(LL i=1;i<=n;++i) Bit.add(T.dfn[pos[i]],1);
    for(LL i=1;i<=q;++i){
    	if(fn[i].op==1) T.add(fn[i].pos,fn[i].c);
    	else if(fn[i].op==3) T.Add(fn[i].pos,fn[i].c);
    	else if(fn[i].op==2) T.dele(fn[i].pos);
    	else T.ask();
    }
    return 0;
}

\(G:Link with Monotonic Subsequence\)

对于每个位置,以其为结尾的最长上升子序列和最长下降子序列的长度各个不完全相同,故其下限为 \(\lceil \sqrt{n} \rceil\),考虑构造。
我们可以选连续的 \(\lceil \sqrt{n} \rceil\) 个数降序排列,这样最长下降子序列最长只能取完这些数,最长上升子序列是每一段取一个数也小于等于该值。

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define LF long double
using namespace std;
const int N=1e6+3;
int n,a[N];
IL int in(){
    char c;int f=1;
    while((c=getchar())<'0'||c>'9')
      if(c=='-') f=-1;
    int x=c-'0';
    while((c=getchar())>='0'&&c<='9')
      x=x*10+c-'0';
    return x*f;
}
int main()
{
	int t=in();
	while(t--){
		n=in();
		int x=ceil(sqrt(n));
		for(int i=1;(i-1)*x<n;++i){
			int cnt=0;
			for(int j=(i-1)*x+1;j<=min(i*x,n);++j)
			  a[++cnt]=j;
			reverse(a+1,a+cnt+1);
			for(int j=1;j<=cnt;++j) printf("%d ",a[j]);
		}
		puts("");
	}
    return 0;
}

\(H:Take the Elevator\)

考虑贪心,只考虑上升,一次上升我们肯定想尽可能带 \(r\) 大的人,如果已有 \(m\) 个则找到 \(r\) 小于它们之中 \(l\) 最大值的最大值,如此反复,用 \(set\) 维护即可。
每次的代价为还剩下的人中最高楼层数减 \(1\) 的 \(2\) 倍。

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define LF long double
using namespace std;
const int N=2e5+3,inf=2e9;
struct hh{
	int l,r;
	bool operator<(const hh &a) const{
	return r^a.r?r<a.r:l<a.l;}
};
multiset<hh>st1,st2;
multiset<int>st;
multiset<hh>::iterator it1,it2;
multiset<int>::iterator it;
LL n,m,ans;
IL int in(){
    char c;int f=1;
    while((c=getchar())<'0'||c>'9')
      if(c=='-') f=-1;
    int x=c-'0';
    while((c=getchar())>='0'&&c<='9')
      x=x*10+c-'0';
    return x*f;
}
int main()
{
	int x,y;
	n=in(),m=in();in();
	for(int i=1;i<=n;++i){
		x=in(),y=in();
		if(x<y) st1.insert((hh){x,y});
		else st2.insert((hh){y,x});
	}
	while(st1.size()||st2.size()){
		if(!st1.size()) ans+=(*st2.rbegin()).r*2-2;
		else if(!st2.size()) ans+=(*st1.rbegin()).r*2-2;
		else ans+=2*max((*st2.rbegin()).r,(*st1.rbegin()).r)-2;
		for(int i=1;st1.size()&&i<=m;++i) st.insert((*st1.rbegin()).l),it1=st1.end(),st1.erase(--it1);
		while(st1.size()){
			it1=st1.upper_bound((hh){inf,*st.rbegin()});
			if(it1==st1.begin()) break;
			--it1,it=st.end(),st.erase(--it),st.insert((*it1).l),st1.erase(it1);
		}
		st.clear();
		for(int i=1;st2.size()&&i<=m;++i) st.insert((*st2.rbegin()).l),it2=st2.end(),st2.erase(--it2);
		while(st2.size()){
			it2=st2.upper_bound((hh){inf,*st.rbegin()});
			if(it2==st2.begin()) break;
			--it2,it=st.end(),st.erase(--it),st.insert((*it2).l),st2.erase(it2);
		}
		st.clear();
	}
	cout<<ans<<endl;
    return 0;
}

\(I:let fat tension\)

我们可以用矩阵求出两两向量的 \(le\) 值。
具体的,设 \(X\) 为有 \(n\) 行,每行有一个长度为 \(k\) 的向量(为初始向量的单位向量)的矩阵。
\(Y\) 为有 \(n\) 行,每行为 \(y\) 向量的矩阵。
\(S=X \cdot X^T \cdot Y\) ,\(S\) 即为答案。
三个矩阵分别为 \(n \times k, k \times n, n \times d\),我们用结合律先算 \(X^T \cdot Y\),再算前面的 \(X\) 。如此复杂度为 \(O(nkd)\) 。

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define LF double
using namespace std;
const int N=1e4+3,M=55;
int n,k,d,a[N][M],b[N][M];
IL int in(){
    char c;int f=1;
    while((c=getchar())<'0'||c>'9')
      if(c=='-') f=-1;
    int x=c-'0';
    while((c=getchar())>='0'&&c<='9')
      x=x*10+c-'0';
    return x*f;
}
struct hh{
	LF *a[N];int R,C;
	IL void init(int x,int y){
		for(int i=1;i<=x;++i){
			a[i]=new LF[y+1];
			for(int j=1;j<=y;++j) a[i][j]=0;
		}
		R=x,C=y;
	}
	hh operator*(const hh b) const{
		hh c;c.init(R,b.C);
		for(int i=1;i<=R;++i)
		  for(int j=1;j<=b.C;++j)
		    for(int k=1;k<=C;++k)
		      c.a[i][j]+=a[i][k]*b.a[k][j];
		return c;
	}
}A,B,C;
int main()
{
	n=in(),k=in(),d=in();
	A.init(n,k);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=k;++j)
		  A.a[i][j]=in();
	B.init(k,n);
	for(int i=1;i<=n;++i){
		LF Len=0;
		for(int j=1;j<=k;++j) Len+=A.a[i][j]*A.a[i][j];
		Len=sqrt(Len);
		for(int j=1;j<=k;++j) B.a[j][i]=(A.a[i][j]/=Len);
	}
	C.init(n,d);
	for(int i=1;i<=n;++i)
	  for(int j=1;j<=d;++j)
	    C.a[i][j]=in();
	B=B*C,A=A*B;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=d;++j)
		  printf("%.8lf ",A.a[i][j]);
		printf("\n");
	}
    return 0;
}

\(J:Link with Arithmetic Progression\)

刚刚以为摆脱高考的 \(naive\) 再次回想起被它支配的恐惧。。。
线性回归这种垃圾问题能不能别考啊。。。又没思维难度。。。就让你对一堆数嗯算。。。

\(K:Link with Bracket Sequence I\)

考虑 \(dp\) ,\(f_{i,j,k}\) 表示现在填了 \(b\) 中 \(i\) 个字符,填了 \(a\) 中 \(j\) 个字符,且左括号比右括号多的方案数。转移很明显,但是要注意去重,具体策略是能填 \(a\) 中的就填 \(a\) 中的,尽量不直接加括号。

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define lf long double
using namespace std;
const int M=2e2+3,N=1e5+3,p=1e9+7;
int n,m,f[M][M][M];
char a[N];
IL int in(){
    char c;int f=1;
    while((c=getchar())<'0'||c>'9')
      if(c=='-') f=-1;
    int x=c-'0';
    while((c=getchar())>='0'&&c<='9')
      x=x*10+c-'0';
    return x*f;
}
IL int mod(int x){return x>=p?x-p:x;}
IL void solve(){
	n=in(),m=in();
	memset(f,0,sizeof(f));
	f[0][0][0]=1;
	scanf("%s",a+1);
	for(int i=0;i<=m;++i){
		for(int j=0;j<=n;++j)
		  for(int k=0;k<=(m>>1);++k){
		  	if(j<n){
		  		if(a[j+1]=='('){
		  			if(k) f[i+1][j][k-1]=mod(f[i+1][j][k-1]+f[i][j][k]);
		  			f[i+1][j+1][k+1]=mod(f[i+1][j+1][k+1]+f[i][j][k]);
		  		}
		  		else{
		  			if(k) f[i+1][j+1][k-1]=mod(f[i+1][j+1][k-1]+f[i][j][k]);
		  			f[i+1][j][k+1]=mod(f[i+1][j][k+1]+f[i][j][k]);
		  		}
		  	}
		  	else{
		  		f[i+1][j][k+1]=mod(f[i+1][j][k+1]+f[i][j][k]);
		  		if(k) f[i+1][j][k-1]=mod(f[i+1][j][k-1]+f[i][j][k]);
		  	}
		  }
	}
	printf("%d\n",f[m][n][0]);
}
int main()
{
    int T=in();
    while(T--) solve();
    return 0;
}

\(L:Link with Level Editor I\)

令 \(f_{i,j}\) 为在 \(i\) 世界中,从最大哪个世界开始可以到达 \(j\) 节点。根据边 \(dp\) 即可,滚动数组优化空间。

#define IL inline
#define LL long long
using namespace std;
const int N=1e4+3,inf=1e9;
int n,m,f[2][N],ans=inf;
IL int in(){
	char c;int f=1;
	while((c=getchar())<'0'||c>'9')
	  if(c=='-') f=-1;
	int x=c-'0';
	while((c=getchar())>='0'&&c<='9')
	  x=x*10+c-'0';
	return x*f;
}
int main()
{
	n=in(),m=in();
	for(int i=2;i<=m;++i) f[0][i]=-inf;
	for(int i=1,op=1;i<=n;++i,op^=1){
		int k=in();
		f[op^1][1]=i;
		for(int j=1;j<=m;++j) f[op][j]=f[op^1][j];
		while(k--){
			int x=in(),y=in();
			f[op][y]=max(f[op][y],f[op^1][x]);
		}
		ans=min(ans,i-f[op][m]+1);
	}
	printf("%d\n",ans==inf?-1:ans);
    return 0;
}

标签:int,蔚来,LL,多校,long,牛客,while,IL,define
来源: https://www.cnblogs.com/yiqiAtiya/p/16516070.html