• Mujin Programming Challenge 2017题解


    传送门

    (A)

    似乎并不难啊然而还是没想出来……

    首先我们发现对于一个数(k),它能第一个走到当且仅当对于每一个(i<k)满足(x_igeq 2i-1),这样我们就可以把所有的(i)移到(2i-1)然后让(k)直接一路过去了。而如果对于每个(k)都有这个性质,答案就是(n!)

    所以从左往右扫,记录当前栈中的元素个数,设当前的(k)在栈中的第(i)个位置,且(k)不满足(x_kgeq 2i-1),那么我们必须把前(i)个数中移出至少一个才能使剩下的数满足,所以乘上一个系数就好了。最后栈中是可以任意顺序的

    //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 P=1e9+7;
    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 int 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=2e5+5;
    int x[N],n,res,cnt;
    int main(){
    	scanf("%d",&n),res=1;
    	fp(i,1,n)scanf("%d",&x[i]);
    	fp(i,1,n){
    		if(x[i]<(cnt<<1|1))res=mul(res,cnt+1);
    		else ++cnt;
    	}
    	fp(i,1,cnt)res=mul(res,i);
    	printf("%d
    ",res);
    	return 0;
    }
    

    (B)

    首先如果整个网格全是白色显然(gg)

    对于所有不是整列都黑的列,我们肯定得把它们变得整列都黑。而且不难发现,若是没有整行都黑的一行,我们怎么操作都不可能使得一列变黑。所以答案就是使得某一行变黑的最小次数(+)不是整列都黑的列数

    那么对于一行使其全部变黑的最小次数是多少呢?设这一行为(i),其中有(x)个白色格子,那么如果第(i)列有黑色格子,我们显然可以通过(x)次操作使得这一行全部变黑,否则的话就需要(x+1)次,多出来的那一次就是为了让第(i)列有一个黑色格子

    //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=505;
    char s[N][N];int vis[N],mm[N],n,res,cnt,fl;
    int main(){
    	scanf("%d",&n);
    	fp(i,1,n)scanf("%s",s[i]+1);
    	fp(i,1,n)fp(j,1,n)if(s[i][j]=='#')fl=1,vis[j]=1,++mm[j];
    	if(!fl)return puts("-1"),0;
    	fp(i,1,n)mm[i]=(mm[i]==n?1:0),cnt+=mm[i];
    	res=233333;
    	fp(i,1,n){
    		R int c=0;
    		fp(j,1,n)c+=s[i][j]=='.';
    		cmin(res,n-cnt+c+(vis[i]^1));
    	}
    	printf("%d
    ",res);
    	return 0;
    } 
    

    (C)

    首先,我们定义(nxt(i))表示最小的(j)满足([i,j))操作完之后为空,同理定义(nxt_{a,b,c...}(i))表示最小的(j)满足([i,j))操作完之后为(a,b,c....)。初始时全都设为(inf)

    如果(s[i]=x),那么显然有(nxt_x(i)=i+1),那么其它的该怎么计算呢?

    我们发现一次操作的过程相当于维护了一个栈,每一次如果当前元素和栈顶元素相等就把栈顶元素删除或者变成下一个字母。以(y)为例,如果([i,j))操作完后是(y),那么必然有([i+1,j))操作完之后是(x),然后(s[i])和那个剩下的(x)一起变成(y),即有(s_y(i)=s_x(s_x(i)))。同理有(nxt(i)=nxt_z(nxt_z(i)))

    然而对于那些字典序小于(x)的字母呢?显然它们至少得等到(s[i])被消掉,所以有(nxt_c(i)=nxt_c(nxt(i)))

    从后往前递推,然后倍增维护一下(nxt(i))就好了

    //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;
    char s[N];int nxt[N][35],to[N][25],n,q;
    int main(){
    	scanf("%s",s+1),n=strlen(s+1);
    	fp(i,0,26)nxt[n][i]=nxt[n+1][i]=n+1;
    	fp(i,0,19)to[n][i]=to[n+1][i]=n+1;
    	fd(i,n-1,0){
    		R int c=s[i+1]-'a';
    		nxt[i][c]=i+1;
    		fp(j,c+1,26)nxt[i][j]=nxt[nxt[i][j-1]][j-1];
    		fp(j,0,c-1)nxt[i][j]=nxt[nxt[i][26]][j];
    		to[i][0]=nxt[i][26];
    	}
    	fp(j,1,19)fp(i,0,n-1)to[i][j]=to[to[i][j-1]][j-1];
    	scanf("%d",&q);
    	for(int l,r,pos;q;--q){
    		scanf("%d%d",&l,&r);
    		pos=--l;
    		fd(j,19,0)if(to[pos][j]<=r)pos=to[pos][j];
    		puts(pos==r?"Yes":"No");
    	}
    	return 0;
    }
    

    (D)

    好神仙的题……

    首先,我们随便选一个点令其(h(rt)=0),对于每条边,如果定向为(u o v)(h(v)=h(u)+1),然后(dfs)一遍把所有点的(h)求出来,那么题中的东西就可以转化成(d(s,t)={dist(s,t)+h(s)-h(t)over 2})

    不难发现题中(D)的最大值就是直径除以二上取整,那么代入上式即可得到对于所有点对都需要满足(|h(s)-h(t)|leq 2D-dist(s,t))

    如果直径是偶数,我们取直径的中点(rt),那么所有距离直径为(D)的点(h)应该相同,不妨假设它们全为(0),此时可以发现对于树上任意一点(u)需要满足(|h(u)|leq D-dist(r,u))

    必要性有了,充分性也可以证明

    [egin{aligned} |h(u)-h(v)|leq |h(u)|+|h(v)|leq 2D-dist(r,u)-dist(r,v)leq 2D-dist(u,v) end{aligned} ]

    所以只要所有点满足(|h(u)|leq D-dist(r,u)),然后做一个(dp)就好了

    如果直径是奇数的话,那么中点有两个,分别设为(s,t),并令(s)子树中那些为白点,(t)子树中那些为黑点

    那么条件变成

    如果(u)是白点则(|h(u)|leq D-1-dist(s,u)),如果(u)是黑点则(|h(u)|leq D-dist(t,u))

    或者

    如果(u)是白点则(|h(u)|leq D-dist(s,u)),如果(u)是黑点则(|h(u)|leq D-1-dist(t,u))

    因为两种情况里直径的一端必然全部标号为(0),而另一端根据直径的奇偶性显然不会为(0),所以标号是不会被重复计算的

    然而两种情况的确有交集,比方说白点这边所有直径的端点全是(0),黑点这边直径的端点全是(-1),那么整棵树上所有值(+1)之后标号不同但是等价,被我们多数了,需要减掉

    交集分别是

    如果(u)是白点则(|h(u)|leq D-1-dist(s,u)),如果(u)是黑点则(|h(u)+1|leq D-1-dist(t,u))

    或者

    如果(u)是白点则(|h(u)|leq D-1-dist(s,u)),如果(u)是黑点则(|h(u)-1|leq D-1-dist(t,u))

    //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 P=1e9+7;
    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 int 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=1005,V=505;
    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 fa[N],dis[N],f[N][N<<1],n,D;
    void dfs(int u){go(u)if(v!=fa[u])dis[v]=dis[u]+1,fa[v]=u,dfs(v);}
    void get(int u,int lim,int ex){
    	go(u)if(v!=fa[u])get(v,lim,ex);
    	R int l=dis[u]-lim+ex,r=lim-dis[u]+ex;
    	fp(k,l+V,r+V){
    		f[u][k]=1;
    		go(u)if(v!=fa[u])f[u][k]=mul(f[u][k],add(f[v][k-1],f[v][k+1]));		
    	}
    }
    inline int even(R int s){
    	dis[s]=fa[s]=0,memset(f,0,sizeof(f));
    	dfs(s),get(s,D,0);
    	R int res=0;
    	fp(i,0,V<<1)upd(res,f[s][i]);
    	return res;
    }
    inline int odd(R int s,R int t,R int ex){
    	fa[s]=t,fa[t]=s,dis[s]=dis[t]=0;
    	memset(f,0,sizeof(f));
    	dfs(s),dfs(t);
    	get(s,D+(!ex),0),get(t,D,ex);
    	R int res=0;
    	fp(i,1,V<<1)upd(res,mul(f[s][i],add(f[t][i-1],f[t][i+1])));
    	return res;
    }
    int main(){
    	scanf("%d",&n);
    	for(R int i=1,u,v;i<n;++i)scanf("%d%d",&u,&v),Add(u,v),Add(v,u);
    	dis[1]=fa[1]=0,dfs(1);
    	R int rt=1;
    	fp(i,1,n)if(dis[i]>dis[rt])rt=i;
    	dis[rt]=fa[rt]=0,dfs(rt);
    	fp(i,1,n)if(dis[i]>dis[rt])rt=i;
    	if(dis[rt]&1^1){
    		D=dis[rt]>>1;
    		fp(i,1,D)rt=fa[rt];
    		printf("%d
    ",even(rt));
    	}else{
    		D=dis[rt]>>1;
    		fp(i,1,D)rt=fa[rt];
    		R int s=rt,t=fa[s];
    		R int res=add(odd(s,t,0),odd(t,s,0));
    		res=dec(res,add(odd(s,t,1),odd(s,t,-1)));
    		printf("%d
    ",res);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Android开发总结
    LeakCanary原理分析
    机器学习
    Kivy 中文教程 实例入门 简易画板 (Simple Paint App):2. 实现绘图功能
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 3. 循环
    Kivy 中文教程 实例入门 简易画板 (Simple Paint App):1. 自定义窗口部件 (widget)
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 2. 变量
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 1. 神秘朋友
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 0. 准备工作
    远程显示(操作) 服务器 GUI 程序(图形化界面) (基于 X11 Forwarding + Centos + MobaXterm)
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11532902.html
Copyright © 2020-2023  润新知