• CODE FESTIVAL 2017 Final题解


    传送门

    (A)

    咕咕

    const int N=55;
    const char to[]={"AKIHABARA"};
    char s[N];int n;
    int main(){
    	scanf("%s",s),n=strlen(s);
    	R int i=0,j=0;
    	for(;i<n;){
    		while(j<9&&s[i]!=to[j]&&to[j]=='A')++j;
    		if(j>=9||s[i]!=to[j])return puts("NO"),0;
    		++i,++j;
    	}
    	while(j<9&&to[j]=='A')++j;
    	puts(j==9?"YES":"NO");
    	return 0;
    }
    

    (B)

    为了不回文,必须得(ABCABC...)这样放,那么三个字母出现次数之差不能超过(1)

    const int N=1e5+5;
    char s[N];int cnt[3],n,mx;
    int main(){
    	scanf("%s",s+1),n=strlen(s+1);
    	fp(i,1,n)++cnt[s[i]-'a'];
    	mx=max(cnt[0],max(cnt[1],cnt[2]));
    	if(!cnt[0]||!cnt[1]||!cnt[2])return puts(mx<=1?"YES":"NO"),0;
    	fp(i,0,2)if(cnt[i]<mx-1)return puts("NO"),0;
    	puts("YES");
    	return 0;
    }
    

    (C)

    首先每个(d)的出现次数不能超过(2)否则答案必定为(0),那么接下来记录一下每个(d)的出现次数然后爆搜每个(d)是位于(d)还是(24-d)就行了

    //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=35,inf=0x3f3f3f3f;
    int cnt[N],n,res;
    inline int min(R int x,R int y){return x<y?x:y;}
    inline int abs(R int x){return x<0?-x:x;}
    void dfs(int pos,int s,int ret){
    	if(ret<=res)return;
    	if(pos==12){
    		if(cnt[12]){
    			fp(i,1,23)if(s>>i&1)cmin(ret,abs(12-i));
    			cmin(ret,12);
    		}
    		cmax(res,ret);
    		return;
    	}
    	if(!cnt[pos])return dfs(pos+1,s,ret),void();
    	cmin(ret,pos);
    	if(cnt[pos]==2){
    		fp(i,1,23)if(s>>i&1)cmin(ret,abs(pos-i)),cmin(ret,abs(24-pos-i));
    		cmin(ret,abs(pos-(24-pos)));
    		dfs(pos+1,s|(1<<pos)|(1<<(24-pos)),ret);
    		return;
    	}
    	R int tmp=ret;
    	fp(i,1,23)if(s>>i&1)cmin(tmp,abs(pos-i));
    	dfs(pos+1,s|(1<<pos),tmp);
    	tmp=ret;
    	fp(i,1,23)if(s>>i&1)cmin(tmp,abs(24-pos-i));
    	dfs(pos+1,s|(1<<(24-pos)),tmp);
    }
    int main(){
    	scanf("%d",&n);
    	for(R int i=1,x;i<=n;++i)scanf("%d",&x),++cnt[x];
    	if(cnt[0]||cnt[12]>1)return puts("0"),0;
    	fp(i,1,11)if(cnt[i]>2)return puts("0"),0;
    	dfs(1,0,inf);
    	printf("%d
    ",res);
    	return 0;
    }
    

    (D)

    首先选的顺序肯定是按(h_i+p_i)排序之后再选最优的(证明的话,可以考虑一个合法的选的序列,对于每一个(i)必须满足(sum_{jleq i} hleq h_i+p_i),因为前缀和递增所以(h_i+p_i)也必须非降才是),然后直接(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;
    typedef long long ll;
    const int N=5005;const ll inf=0x3f3f3f3f3f3f3f3f;
    int a[N],b[N],c[N],id[N],n;ll mn[N];
    inline bool cmp(const int &x,const int &y){return c[x]==c[y]?b[x]<b[y]:c[x]<c[y];}
    int main(){
    	scanf("%d",&n);
    	fp(i,1,n)scanf("%d%d",&a[i],&b[i]),c[i]=a[i]+b[i],id[i]=i;
    	sort(id+1,id+1+n,cmp);
    	memset(mn,0x3f,sizeof(mn));
    	mn[0]=0;
    	fp(i,1,n){
    		R int t=n-1;
    		while(mn[t]==inf)--t;
    		fd(j,t,0)if(mn[j]<=a[id[i]])cmin(mn[j+1],mn[j]+b[id[i]]);
    	}
    	fd(i,n,0)if(mn[i]!=inf)return printf("%d
    ",i),0;
    	return 0;
    }
    

    (E)

    差分之后等价于每次(l_i)位置(+1)(r_i+1)位置(-1),而此时回文的条件就是对于每个(i)(a_i+a_{n-2-i})(26)的倍数(最中间位置除外),因为每个连通块的权值之和不变,所以要满足每个连通块权值之和为(26)的倍数(包含最中间位置那个连通块除外)

    //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=1e5+5;
    char s[N];int fa[N],b[N],sum[N],vis[N],n,m;
    inline int val(R int x){return x<0?x+26:x;}
    inline int find(R int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    int main(){
    	scanf("%s",s+1),n=strlen(s+1);
    	fp(i,1,n+1)s[i]-='a',fa[i]=i;
    	s[n+1]=0;
    	scanf("%d",&m);
    	for(R int i=1,l,r;i<=m;++i)scanf("%d%d",&l,&r),fa[find(r+1)]=find(l);
    	fp(i,1,n+1)b[i]=val(s[i]-s[i-1]);
    	fp(i,1,(n+1)>>1)fa[find(n+2-i)]=find(i);
    	fp(i,1,n+1)sum[find(i)]+=b[i];
    	if((n+1)&1)vis[find((n+2)>>1)]=1;
    	fp(i,1,n+1)if(i==find(i)&&sum[i]%26)
    		if(!vis[i])return puts("NO"),0;
    	puts("YES");
    	return 0;
    } 
    

    (F)

    手玩了一下(k)较小的情况去考虑对应的(n)……

    首先对于前(k)行,每行都令(1)开头,剩下的数依次从(2)取到(n),这样的话总共需要(n=k(k-1)+1)个数,且第(i)行的第(2)(k)个数依次是((i-1)(k-1)+2)(i(k-1)+1),后面为了方便起见用(st[i][j])表示第(i)行第(j)列的数

    对于接下来的每(k)行,依次令(st[1][j])开头,然后在(2)(k)行中各取一个数,共取(k-1)组,要保证选出的(k-1)组中任意两组没有重复的数,那么只要以(j-1)为步长,以第(2)行中的第(2)到第(k)个数开头,就能保证取完下面所有数了

    然而对于两组开头不一样的行,直接这样取可能会有大于一个重复的情况,注意到一种步长会导致这种情况当且仅当步长为(k-1)的因子,那么只要保证(k-1)是个质数就行了,取(k=38)即可

    具体细节可以看代码理解

    //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=1e5+5;
    vector<int>st[N];int n,k,tot;
    inline int add(R int x,R int y){return x+y>=k-1?x+y-k+1:x+y;}
    int main(){
    	k=38,n=k*(k-1)+1,tot=1;
    	printf("%d %d
    ",n,k);
    	fp(i,1,k){
    		printf("%d ",1);
    		st[i].resize(k-1);
    		fp(j,0,k-2)st[i][j]=++tot;
    		fp(j,0,k-2)printf("%d ",st[i][j]);
    		puts("");
    	}
    	fp(i,0,k-2){
    		fp(s,0,k-2){
    			R int p=s;
    			printf("%d ",st[1][i]);
    			fp(j,2,k)printf("%d ",st[j][p]),p=add(p,i);
    			puts("");
    		}
    	}
    	return 0;
    }
    

    (G)

    发现要让剩下的石子最少,每次一定是取从左往右第一个可以操作的操作

    (f_i)表示(i)(n)的数中总的操作次数,如果(a_ileq i),那么(f_i=f_{i+1}+(f_{i+1}+a_i)/i)(下取整),最终的分数就是(sum a_i-f_1)

    前面总和很好求,所以我们转化为来求(sum f_1)

    注意到上面的转移之和(f_{i+1})的值有关,且(f_{i})(O(n^2))级别的,那么我们记(s_{i,j})表示对于(i)(f_i=j)的方案数,然后枚举当前数是什么,转移即可

    复杂度(O(n^4))

    //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=105,M=N*N;
    int f[N][M],dv[N][M],n,p,s,res,sum;
    int main(){
    	scanf("%d%d",&n,&p);
    	f[n][0]=1;
    	fp(i,1,n)fp(j,1,n*n/i)dv[i][i*j]=j;
    	fp(i,1,n)fp(j,1,n*n)if(!dv[i][j])dv[i][j]=dv[i][j-1];
    	fd(i,n,1){
    		if(p>i)fp(j,0,s)f[i-1][j]=mul(f[i][j],p-i);
    		R int t=s;
    		fp(j,0,s)if(f[i][j])fp(k,0,min(p,i)){
    			upd(f[i-1][j+dv[i][j+k]],f[i][j]);
    			cmax(t,j+dv[i][j+k]);
    		}
    		s=t;
    	}
    	fp(i,0,s)upd(res,mul(i,f[0][i]));
    	sum=mul(n*p*(p+1)>>1,ksm(p+1,n-1));
    	printf("%d
    ",dec(sum,res));
    	return 0;
    } 
    

    (H)

    首先发现一格如果左上,右上,左下,右下四个方位中的一个没有冰山,那么他最终肯定会消失

    还有另一种方法是把一个大矩形分成四块,并把左上和右下或右上和左下的冰山全部消掉,然后剩下的两个部分互不影响了

    那么设(f[lx][rx][ly][]ry])表示删到只剩矩形([lx,ly,rx,ry])中的元素时,最少要删多少,然后枚举在哪里分割矩形转移即可

    具体可以看题解的图理解,复杂度(O(n^6))

    记忆化搜索+剪枝似乎比直接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=45,inf=0x3f3f3f3f;
    char a[N][N];int f[N][N][N][N],s[N][N];
    int px,py,n,m,res;
    inline int qs(R int x,R int y,R int xx,R int yy){
    	return s[xx][yy]+s[x-1][y-1]-s[xx][y-1]-s[x-1][yy];
    }
    inline int min(R int x,R int y){return x<y?x:y;}
    inline int get(R int lx,R int ly,R int rx,R int ry){
    	return min(min(qs(lx,ly,px,py),qs(px,py,rx,ry)),
    			min(qs(lx,py,px,ry),qs(px,ly,rx,py)));
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d%d",&n,&m);
    	fp(i,1,n)scanf("%s",a[i]+1);
    	fp(i,1,n)fp(j,1,m){
    		if(a[i][j]=='P')px=i,py=j;
    		s[i][j]=s[i-1][j]+s[i][j-1]+(a[i][j]=='#')-s[i-1][j-1];
    	}
    	memset(f,0x3f,sizeof(f));
    	f[1][1][n][m]=0,res=inf;
    	fp(lx,1,n)fp(ly,1,m)fd(rx,n,lx)fd(ry,m,ly)
    		if(f[lx][ly][rx][ry]!=inf){
    			R int ret=f[lx][ly][rx][ry];
    			if(px>=lx&&px<=rx&&py>=ly&&py<=ry)cmin(res,ret+get(lx,ly,rx,ry));
    				else cmin(res,ret);
    			ret+=qs(lx,ly,rx,ry);
    			fp(x,lx,rx+1)fp(y,ly,ry+1)
    				if(!((x==lx||x==rx+1)&&(y==ly&&y==ry+1))){
    					R int val=qs(lx,ly,x-1,y-1)+qs(x,y,rx,ry);
    					if(x>lx&&y>ly&&!(px>=x&&px<=rx&&py>=y&&py<=ry))
    						cmin(f[lx][ly][x-1][y-1],ret-val);
    					if(x<=rx&&y<=ry&&!(px<x&&px>=lx&&py<y&&py>=ly))
    						cmin(f[x][y][rx][ry],ret-val);
    					val=qs(lx,y,x-1,ry)+qs(x,ly,rx,y-1);
    					if(x>lx&&y<=ry&&!(px>=x&&px<=rx&&py<y&&py>=ly))
    						cmin(f[lx][y][x-1][ry],ret-val);
    					if(x<=rx&&y>ly&&!(px<x&&px>=lx&&py>=y&&py<=ry))
    						cmin(f[x][ly][rx][y-1],ret-val);
    				}
    		}
    	printf("%d
    ",res);
    	return 0;
    }
    

    (I)

    方便起见把人和排名都设为(0)(2^n-1)

    (a_i)表示排名为(i)的人,则对于任意(k),有(a_ileq a_{i|2^k}(2^k otin i))

    这个考虑归纳证明,不妨令(i=2p),则排名为(i)的人就是左边排名为(p)的人和右边排名为(p)的人里较小的那个,而排名为(i|2^k)的是左边排名为(p+2^{k-1})和右边排名为(p+2^{k-1})的,根据归纳证明不管左边右边都有(a_pleq a_{p|2^{k-1}}),所以有(a_ileq a_{i|2^k})成立

    然后我们对于每个(i)(a_i)的取值范围求出来,之后扫描线加贪心求出每个(a_i)的值就好了,最后原序列中第(i)位的值就是将(i)二进制翻转之后的(a)(类似于(FFT)的蝴蝶操作)

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define pb emplace_back
    #define fi first
    #define se second
    #define gg return puts("NO"),0;
    #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 pair<int,int> pi;
    const int N=25,L=(1<<18)+5;
    int low[L],hig[L],r[L],val[L];
    vector<pi>vc[L];int n,lim;
    priority_queue<pi,vector<pi>,greater<pi> >q;
    int main(){
    	scanf("%d",&n),lim=(1<<n);
    	fp(i,0,lim-1){
    		scanf("%d",&val[i]),--val[i];
    		r[i]=(r[i>>1]>>1)|((i&1)<<(n-1));
    	}
    	fd(i,lim-1,0){
    		hig[i]=(~val[i]?val[i]:lim-1);
    		fp(k,0,n-1)if(i>>k&1^1)cmin(hig[i],hig[i|(1<<k)]);
    	}
    	fp(i,0,lim-1){
    		if(~val[i])low[i]=val[i];
    		fp(k,0,n-1)if(i>>k&1)cmax(low[i],low[i^(1<<k)]);
    	}
    	fp(i,0,lim-1){
    		if(low[i]>hig[i])gg;
    		vc[low[i]].pb(pi(hig[i],i));
    	}
    	fp(i,0,lim-1){
    		for(auto v:vc[i])q.push(v);
    		if(q.empty()||q.top().fi<i)gg;
    		val[q.top().se]=i,q.pop();
    	}
    	puts("YES");
    	fp(i,0,lim-1)printf("%d ",val[r[i]]+1);
    	return 0;
    }
    

    (J)

    据说可以直接点分暴艹……

    就是对于当前点分中心,找到连上的边边权最小的那个点,再把当前连通块中所有点和那个点连边,最后跑一遍(kruskal)即可

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define pb emplace_back
    #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=2e5+5;
    struct node{
    	int u,v;ll w;
    	inline node(R int uu,R int vv,R ll ww):u(uu),v(vv),w(ww){}
    	inline bool operator <(const node &b)const{return w<b.w;}
    };vector<node>E;vector<int>st;
    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;}
    ll dis[N];int sz[N],vis[N],mx[N],fa[N],a[N];
    int n,rt,size;
    inline int find(R int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    void findrt(int u,int fa){
    	sz[u]=1,mx[u]=0;
    	go(u)if(!vis[v]&&v!=fa){
    		findrt(v,u);
    		sz[u]+=sz[v],cmax(mx[u],sz[v]);
    	}
    	cmax(mx[u],size-sz[u]);
    	if(mx[u]<mx[rt])rt=u;
    }
    void get(int u,int fa){
    	st.pb(u);
    	go(u)if(!vis[v]&&v!=fa)dis[v]=dis[u]+e[i].w,get(v,u);
    }
    void solve(int u){
    	vis[u]=1,dis[u]=0,st.clear(),get(u,0);
    	R int p=0;R ll mn=1e18;
    	for(auto v:st)if(cmin(mn,dis[v]+=a[v]))p=v;
    	for(auto v:st)E.pb(node(p,v,dis[p]+dis[v]));
    	R int s=size;
    	go(u)if(!vis[v]){
    		rt=0,size=(sz[v]<=sz[u]?sz[v]:s-sz[u]);
    		findrt(v,0),solve(rt);
    	}
    }
    int main(){
    	scanf("%d",&n),mx[0]=n+1;
    	fp(i,1,n)scanf("%d",&a[i]);
    	for(R int i=1,u,v,w;i<n;++i){
    		scanf("%d%d%d",&u,&v,&w);
    		add(u,v,w),add(v,u,w);
    	}
    	rt=0,size=n,findrt(1,0),solve(rt);
    	sort(E.begin(),E.end());
    	fp(i,1,n)fa[i]=i;
    	R ll res=0;
    	for(auto e:E)if(find(e.u)!=find(e.v))res+=e.w,fa[find(e.u)]=find(e.v);
    	printf("%lld
    ",res);
    	return 0;
    }
    
  • 相关阅读:
    bzoj 3308 九月的咖啡店
    8.13模拟赛
    8.10模拟赛
    8.9模拟赛
    8.8模拟赛
    Codeforces Round #406 (Div. 2) D. Legacy (线段树建图dij)
    BZOJ 2957: 楼房重建 (分块)
    SPOJ BGSHOOT
    Codeforces Round #404 (Div. 2) E. Anton and Permutation(树状数组套主席树 求出指定数的排名)
    Lightoj-1356 Prime Independence(质因子分解)(Hopcroft-Karp优化的最大匹配)
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11643872.html
Copyright © 2020-2023  润新知