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\n",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\n",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\n",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]='\n';
}
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\n",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\n",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\n",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\n",x);
fd(i,31,0)if(x>>i&1){
// printf("%d %d %d\n",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\n",res);
}
return 0;
}