2019牛客暑期多校训练营(第四场)题解

传送门

按做题的顺序写好了……

(K)

对于(0)的情况单独考虑,剩下的一定是后面两个或更多的(0)加上前面一堆是(3)的倍数的数,后面(0)的情况可以预处理一下,前面只要记一下模(3)分别余(0/1/2)的数各有多少个就可以了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
const int N=5e5+5;
char s[N];int Pre[N],suf[N],sum[N],g[N],las[3],n;ll res;
int main(){
	scanf("%s",s+1),n=strlen(s+1);
	fp(i,1,n)Pre[i]=(s[i-1]=='0'?Pre[i-1]+1:0);
	fd(i,n,1)suf[i]=(s[i+1]=='0'?suf[i+1]+1:0);
	fp(i,1,n)if(s[i]=='0'&&s[i+1]!='0')res+=1ll*(Pre[i]+1)*(Pre[i]+2)>>1;
	fp(i,1,n)sum[i]=(sum[i-1]+(s[i]-'0'))%3;
	las[0]=1;
	fp(i,1,n)g[i]=las[sum[i]],las[sum[i]]=g[i]+1;
	for(R int i=1;i<=n;++i)if(s[i]!='0'&&suf[i]>=2)res+=1ll*g[i]*(suf[i]-1);
	printf("%lld
",res);
	return 0;
}

(J)

跑分层最短路,记(dis[i][j])表示走了(i)条免费边,到达(j)的最短路长度,对于每一个(i),先把内部跑完再拓展到(i+1)的情况就好了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=1005;
struct eg{int v,nx,w;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v,R int w){e[++tot]={v,head[u],w},head[u]=tot;}
int dis[N][N],vis[N][N],n,m,S,T,k;
struct node{
	int u,p,d;
	inline node(){}
	inline node(R int uu,R int pp,R int dd):u(uu),p(pp),d(dd){}
	inline bool operator <(const node &b)const{return d==b.d?p>b.p:d>b.d;}
};priority_queue<node>q;
void spfa(){
	memset(dis,0x3f,sizeof(dis));
	dis[S][0]=0;q.push(node(S,0,0));
	while(!q.empty()){
		int u=q.top().u,p=q.top().p;q.pop();if(vis[u][p])continue;
		vis[u][p]=1;
		if(u==T&&p==k)return;
		go(u){
			if(cmin(dis[v][p],dis[u][p]+e[i].w))q.push(node(v,p,dis[v][p]));
			if(p<k&&cmin(dis[v][p+1],dis[u][p]))q.push(node(v,p+1,dis[v][p]));
		}
	}
}
int main(){
	scanf("%d%d%d%d%d",&n,&m,&S,&T,&k);
	for(R int i=1,u,v,w;i<=m;++i)scanf("%d%d%d",&u,&v,&w),add(u,v,w),add(v,u,w);
	spfa();
	printf("%d
",dis[T][k]);
	return 0;
}

(A)

(zzq)说这题很简单,记(d)为最远的两点之间的距离,答案就是(d/2)上取整,证明显然

然后我跟个傻逼一样打了个换根(dp)来算……

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=5e5+5,inf=0x3f3f3f3f;
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int dis[N],fr[N],dd[N],vis[N],n,k,res=inf;
inline int max(R int x,R int y){return x>y?x:y;}
void dfs1(int u,int fa){
	dis[u]=(vis[u]?0:-inf),dd[u]=-inf,fr[u]=u;
	go(u)if(v!=fa){
		dfs1(v,u);
		if(dis[v]+1>dis[u])dd[u]=dis[u],dis[u]=dis[v]+1,fr[u]=v;
		else cmax(dd[u],dis[u]+1);
	}
}
void dfs2(int u,int fa,int las){
	cmin(res,max(las,dis[u]));
	go(u)if(v!=fa){
		if(v==fr[u])dfs2(v,u,max(las,dd[u])+1);
		else dfs2(v,u,max(las,dis[u])+1);
	}
}
int main(){
	scanf("%d%d",&n,&k);
	for(R int i=1,u,v;i<n;++i)scanf("%d%d",&u,&v),add(u,v),add(v,u);
	for(R int x,i=1;i<=k;++i)scanf("%d",&x),vis[x]=1;
	dfs1(1,0);
	dfs2(1,0,-inf);
	if(!k)res=0;
	printf("%d
",res);
	return 0;
}

(D)

我好像根本没看到那个“输出尽可能少的数”……

对于每一个二进制位,模(3)之后肯定是余(1)(2),我们把所有有(1)的二进制位按模(3)(1)还是余(2)分成两类,记为(A)(B),那么一个合法的数必然是在(A)中取(3)的倍数个(1)或在(B)中取(3)的倍数个(1)或在(AB)中同时取相同个数的(1),搞一搞就可以了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R ll x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]=' ';
}
const int N=1e5+5;
int st[2][N],top[2],T;ll q[N],t,n;
int main(){
	for(scanf("%d",&T);T;--T){
		scanf("%lld",&n);t=top[0]=top[1]=0;
		for(R int i=0;n>>i;++i)if(n>>i&1)st[i&1][top[i&1]]=i,++top[i&1];
		if(top[0]&&top[1]){
			R int k=max(top[0],top[1]);
			for(R int i=0,j=0;k;--k,i=(i+1)%top[0],j=(j+1)%top[1])
				q[++t]=(1ll<<st[0][i])|(1ll<<st[1][j]);
		}else if(top[0])fp(i,2,top[0]-1)q[++t]=(1ll<<st[0][i])|(1ll<<st[0][1])|(1ll<<st[0][0]);
		else fp(i,2,top[1]-1)q[++t]=(1ll<<st[1][i])|(1ll<<st[1][1])|(1ll<<st[1][0]);
		print(t);
		fp(i,1,t)print(q[i]);
		sr[++C]='
';
	}
	return Ot(),0;
} 

(C)

我们考虑递归处理,记当前区间为([l,r]),且(p)为区间最小值所在的位置,那么我们需要找到一个跨过(p)的区间且满足(sum_{l',r'})最大(或者最小,看(a_p)是大于(0)还是小于(0)),先直接把(b)做个前缀和之后可以转化为分别在([p,r])([l-1,p-1])的区间中求最大最小值,线段树就行了,做完之后继续递归([l,p-1])([p+1,r])就行了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=3e6+5;const ll inf=4e18;
ll sum[N],res=-4e18;int n,a[N],b[N];
inline ll min(R ll x,R ll y){return x<y?x:y;}
inline ll max(R ll x,R ll y){return x>y?x:y;}
inline int MIN(R int x,R int y){return a[x]<a[y]?x:y;}
struct node;typedef node* ptr;
struct node{
	ptr lc,rc;ll mns,mxs;int mn;
	inline void upd(){mns=min(lc->mns,rc->mns),mxs=max(lc->mxs,rc->mxs),mn=MIN(lc->mn,rc->mn);}
}e[N<<2],*pp=e,*rt;
void build(ptr &p,int l,int r){
	p=++pp;if(l==r)return p->mns=p->mxs=sum[r],p->mn=r,void();
	int mid=(l+r)>>1;
	build(p->lc,l,mid),build(p->rc,mid+1,r);
	p->upd();
}
int mn;ll mxs,mns;
void qmn(ptr p,int l,int r,int ql,int qr){
	if(ql<=l&&qr>=r)return mn=MIN(mn,p->mn),void();
	int mid=(l+r)>>1;
	if(ql<=mid)qmn(p->lc,l,mid,ql,qr);
	if(qr>mid)qmn(p->rc,mid+1,r,ql,qr);
}
void qmns(ptr p,int l,int r,int ql,int qr){
	if(ql<=l&&qr>=r)return cmin(mns,p->mns),void();
	int mid=(l+r)>>1;
	if(ql<=mid)qmns(p->lc,l,mid,ql,qr);
	if(qr>mid)qmns(p->rc,mid+1,r,ql,qr);
}
void qmxs(ptr p,int l,int r,int ql,int qr){
	if(ql<=l&&qr>=r)return cmax(mxs,p->mxs),void();
	int mid=(l+r)>>1;
	if(ql<=mid)qmxs(p->lc,l,mid,ql,qr);
	if(qr>mid)qmxs(p->rc,mid+1,r,ql,qr);
}
void solve(int l,int r){
	if(l>r)return;if(l==r)return cmax(res,1ll*b[r]*a[r]),void();
	mn=0,qmn(rt,1,n,l,r);int p=mn;
	ll a=0,b=0,c=0,d=0;
	mxs=-inf,qmxs(rt,1,n,p,r),a=mxs;
	mns=inf,qmns(rt,1,n,p,r),b=mns;
	if(p>1)mxs=-inf,qmxs(rt,1,n,max(l-1,1ll),p-1),c=mxs;
	if(p>1)mns=inf,qmns(rt,1,n,max(l-1,1ll),p-1),d=mns;
	cmax(res,(a-d)*::a[p]),cmax(res,(b-c)*::a[p]);
	solve(l,p-1),solve(p+1,r);
}
int main(){
//	freopen("testdata.in","r",stdin);
	n=read();a[0]=0x3f3f3f3f;
	fp(i,1,n)a[i]=read();
	fp(i,1,n)b[i]=read(),sum[i]=sum[i-1]+b[i];
	build(rt,1,n);
	solve(1,n);
	printf("%lld
",res);
	return 0;
} 

(I)

我们先直接数出本质不同的子串个数,记为(res),这个可以直接在(SAM)上跑

考虑会被重复计算的子串,必然是自己以及反串都出现过,那么我们对于反串建一个(SAM),并把原串在上面跑,求出“原串与反串的本质不同的公共子串个数”,记为(sum)

这个(sum)里包含什么呢?每一个自己和反串都出现过的子串(T)会在这里面被计数两次,而回文串会在里面的计数一次,需要从答案里减掉的只有(T),而回文串是不用减掉的

那么我们用回文自动机求出本质不同的回文串个数,记为(ret),答案就是(res-(sum-ret)/2)

最尴尬的是我忘了(SAM)(PAM)怎么写只好去抄板子结果连自己写的是啥都不知道了……

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
const int N=7e5+5;
char s[N];int l[N],ch[N][26],fa[N],cc[N],cnt=1,las=1,n;ll res,sum,ret;
void ins(int c){
    int p=las,np=++cnt;las=np,l[np]=l[p]+1;
    for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
    if(!p)fa[np]=1;
    else{
        int q=ch[p][c];
        if(l[q]==l[p]+1)fa[np]=q;
        else{
            int nq=++cnt;l[nq]=l[p]+1;
            memcpy(ch[nq],ch[q],sizeof(ch[q]));
            fa[nq]=fa[q],fa[q]=fa[np]=nq;
            for(;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
        }
    }
}
void clr(){
	memset(fa,0,sizeof(fa)),
	memset(l,0,sizeof(l)),
	memset(ch,0,sizeof(ch)),
	las=cnt=1;
}
void solve(){
	for(R int p=1,i=1,c,len=0;i<=n;++i){
		c=s[i]-'a';
		while(p&&!ch[p][c])p=fa[p],len=l[p];
		++len,p=ch[p][c],cmax(cc[p],len);
	}
}
int c[N],q[N];
void calc(){
	fp(i,1,cnt)++c[l[i]];
	fp(i,1,cnt)c[i]+=c[i-1];
	fd(i,cnt,1)q[c[l[i]]--]=i;
	fd(i,cnt,1)cmax(cc[fa[q[i]]],cc[q[i]]);
	fp(i,1,cnt)cmin(cc[i],l[i]);
	fp(i,1,cnt)sum+=max(0,cc[i]-l[fa[i]]);
}
int len[N],fail[N],tot;
inline int newnode(int x){
    //建立一个新节点,长度为x 
    len[++tot]=x;return tot;
}
inline int getfail(int x,int n){
    //跳fail指针知道找到后缀回文为止 
    while(s[n-len[x]-1]!=s[n]) x=fail[x];
    return x;
}
void QAQ(){
	memset(ch,0,sizeof(ch));
	int last,p,q;
    s[0]=-1,fail[0]=1,last=0;
    len[0]=0,len[1]=-1,tot=1;
    for(int i=1;s[i];++i){
        s[i]-='a';
        //找到可以回文的位置 
        p=getfail(last,i);
        if(!ch[p][s[i]]){
            //如果有了转移就不用建了,否则要新建 
            //前后都加上新字符,所以新回文串长度要加2 
            q=newnode(len[p]+2);
            //因为fail指向的得是原串的严格后缀,所以要从p的fail开始找起 
            fail[q]=ch[getfail(fail[p],i)][s[i]]; 
            //记录转移 
            ch[p][s[i]]=q;
        }
        last=ch[p][s[i]];
    }
//    fp(i,2,tot)ret+=len[i]-len[fail[i]],printf("%d %d %d
",i,len[i],fail[i]);
	ret=tot-1;
}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%s",s+1),n=strlen(s+1);
	fp(i,1,n)ins(s[i]-'a');
	fp(i,1,cnt)res+=l[i]-l[fa[i]];
	clr();
	reverse(s+1,s+1+n);
	fp(i,1,n)ins(s[i]-'a');
	reverse(s+1,s+1+n);
	solve();
	calc();
	QAQ();
	printf("%lld
",res-((sum-ret)>>1));
	return 0;
}

(B)

看一下线性基求交的总结

线段树维护线性基的交就行了

//quming
#include<bits/stdc++.h>
#define R register
#define int unsigned int
#define fp(i,a,b) for(R int i=(a),I=(b);i<=I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b);i>=I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=1e5+5;
struct Base{
	int a[32];
	inline Base(){memset(a,0,sizeof(a));}
	inline Base(const Base &b){memcpy(a,b.a,128);}
	inline int& operator [](const int &x){return a[x];}
	bool ins(int x,bool fl){
		if(!x)return 0;
//		printf("%d
",x);
		fd(i,31,0)if(x>>i&1){
//			printf("%d %d %d
",i,x,fl);
			if(a[i]){
				x^=a[i];
				if(!x)return false;
			}else{
				if(fl)a[i]=x;
				return true;
			}
		}
	}
};
Base merge(Base a,Base b){
	Base na(a),tmp(a),gl;
	int cur,d;
	fp(i,0,31)if(b[i]){
		cur=0,d=b[i];
		fd(j,i,0)if(d>>j&1){
			if(tmp[j]){
				d^=tmp[j],cur^=na[j];
				if(d)continue;
				gl[i]=cur;
			}else tmp[j]=d,na[j]=cur;
			break;
		}
	}
	return gl;
}
struct node;typedef node* ptr;
struct node{
	ptr lc,rc;Base v;
	inline void upd(){v=merge(lc->v,rc->v);}
}e[N<<2],*pp=e,*rt;
int val[N][35],sz[N],n,q;
void build(ptr &p,int l,int r){
	p=++pp;if(l==r){fp(i,1,sz[r])p->v.ins(val[r][i],1);return;}
	int mid=(l+r)>>1;
	build(p->lc,l,mid),build(p->rc,mid+1,r);
	p->upd();
}
int ql,qr,x;
bool query(ptr p,int l,int r){
	if(ql<=l&&qr>=r)return !p->v.ins(x,0);
	int mid=(l+r)>>1;
	if(ql<=mid&&!query(p->lc,l,mid))return false;
	if(qr>mid&&!query(p->rc,mid+1,r))return false;
	return true;
}
signed main(){
//	freopen("testdata.in","r",stdin);
	n=read(),q=read();
	fp(i,1,n){
		sz[i]=read();
		fp(j,1,sz[i])val[i][j]=read();
	}
	build(rt,1,n);
	while(q--)ql=read(),qr=read(),x=read(),puts(query(rt,1,n)?"YES":"NO");
	return 0;
}

(E)

先容斥一下,对于(S),转化为计算最多只有(S)这几位为(1)的方案数

每一个二进制位上模(3)的余数只有(1)或者(2),分别记为(A)类和(B)类,那么若(S)中有(p)(A)类和(q)(B)类,则答案只与(p,q)有关

可以先预处理出(f_{i,j,0/1/2})表示有不超过(i)(A)类的(1),不超过(j)(B)类的(1),总共模(3)(0/1/2)的方案数,那么最多只有(S)这几位为(1)的方案数就可以直接算了

然后容斥一下就行了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
const int P=998244353;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R ll y){
	R int res=1;
	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
	return res;
}
const int N=35;
int f[N][N][3],C[N][N],c[2],T,res,sum;ll n,x;
void init(){
	f[0][0][0]=1;
	fp(i,1,30)fp(k,0,2)upd(f[i][0][k],add(f[i-1][0][k],f[i-1][0][(k+2)%3]));
	fp(i,0,30)fp(j,1,30)fp(k,0,2)upd(f[i][j][k],add(f[i][j-1][k],f[i][j-1][(k+1)%3]));
	C[0][0]=1;
	fp(i,1,30){
		C[i][0]=1;
		fp(j,1,i)C[i][j]=add(C[i-1][j-1],C[i-1][j]);
	}
}
int main(){
	init();
	for(scanf("%d",&T);T;--T){
		scanf("%lld%lld",&n,&x),res=c[0]=c[1]=0;
		for(R int i=0;x>>i;++i)if(x>>i&1)++c[i&1];
		fp(i,0,c[0])fp(j,0,c[1]){
			sum=1ll*C[c[0]][i]*C[c[1]][j]%P*ksm(f[i][j][0],n)%P;
			upd(res,(c[0]+c[1]-i-j)&1?P-sum:sum);
		}
		printf("%d
",res);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/yuanquming/p/11256736.html