[机房测试]10.28

[机房测试]10.28

10.26日原题大战,不是有手就能AK吗?,跳过

欢迎转载ssw02的博客:https://www.cnblogs.com/ssw02/p/11755483.html


地精部落

连接传送门:

T2

模拟一下即可。在画画图找找性质之后发现,由于可以无线次数交换,其实我们要求的可以分为3种情况,记录每种字符出现的次数外加统计特判即可。

分为 两偶数 一奇一偶 两奇数 即可 , 具体看代码

#include<bits/stdc++.h>
using namespace std ;
inline int read(){
	int s=0 ; char g=getchar() ; while(g>'9'||g<'0')g=getchar() ; 
	while( g>='0'&&g<='9' )s=s*10+g-'0',g=getchar() ; return s; 
} 
inline int getc(){
	char g=getchar() ; while( g>'z'||g<'a')g=getchar() ; 
	return (int)g-'a'+1 ;
}
int cnt[30] , N , M , T ;
void  clear(){
	memset( cnt , 0 , sizeof(cnt) ) ;
} 
void  work1(){
	int opt = false ; 
	for( register int i = 1 ; i <= 26 ; ++i )
		if( cnt[i]%4 ){ opt = true ; break ; }
	if( opt )puts("No") ;
	else puts("Yes") ;
}
void  work2(){
	int opt = 0 , tot = ( N/2*2 )*( M/2*2 ) ;
	for( register int i = 1 ; i <= 26 ; ++i )
	    if( cnt[i]%4 == 0 ){
	    	if( cnt[i] > tot )cnt[i] -= tot , tot = 0 ;
	    	else tot -= cnt[i] , cnt[i] = 0 ;
	    }
	    else if( cnt[i] > 4 ){
	    	int rd = cnt[i]/4*4 ;
	    	if( rd > tot )cnt[i] -= tot , tot = 0 ;
	    	else tot -= rd , cnt[i] -= rd ;
	    }
	if( tot ){puts("No");return ;}
	for( register int i = 1 ; i <= 26 ; ++i )
	    if( cnt[i]%2 )opt++ ;
	if( opt ){puts("No");return;}
	puts("Yes") ;
}
void  work3(){
	int opt = 0 , tot = ( N/2*2 )*( M/2*2 ) ;
	for( register int i = 1 ; i <= 26 ; ++i )
	    if( cnt[i]%4 == 0 ){
	    	if( cnt[i] > tot )cnt[i] -= tot , tot = 0 ;
	    	else tot -= cnt[i] , cnt[i] = 0 ;
	    }
	    else if( cnt[i] > 4 ){
	    	int rd = cnt[i]/4*4 ;
	    	if( rd > tot )cnt[i] -= tot , tot = 0 ;
	    	else tot -= rd , cnt[i] -= rd ;
	    }
	if( tot ){puts("No");return ;}
	for( register int i = 1 ; i <= 26 ; ++i )
	    if( cnt[i]%2 )opt++ ;
	if( opt != 1 ){puts("No");return;}
	puts("Yes") ;
}
int main(){
	freopen("quilt.in","r",stdin) ;
	freopen("quilt.out","w",stdout) ;
	T = read() ; 
	while( T-- ){
		N = read() , M = read() ; int m1 ;  
		for( register int i = 1 ; i <= N ; ++i )
		    for( register int j = 1 ; j <= M ; ++j )
		    	m1 = getc() , cnt[m1]++;
		int tag = 0 ;
		tag = (N%2)+(M%2) ;
		if( tag == 0 )work1() ; 
		else if( tag == 1 )work2() ;
		else work3() ;
		clear() ;
	}
	return 0 ;
}

T3

T3相对复杂:直接搬运题解

原创题.
首先每只蚊子的贡献是独立的.如果一只蚊子经过了k个会被灭蚊器影响的点那么这个蚊子对答案的贡献是1-(1-p/q)k.
用1遍bfs求出哪些点是被灭蚊器影响的点.然后进行不同的处理.
n为树的点数,m为叶子节点数.
算法1:
对于每一只蚊子,做一遍bfs,O(m2)次bfs,复杂度O(nm2),期望得分10分
算法2:
通过一次bfs我们可以统计出一个叶子节点出发的所有蚂蚁的贡献.那么只需要m次bfs就可以了.复杂度O(nm).期望得分30分.
算法3:
对于d=0的测试点3,只有1号点被控制.那么经过了1号点的蚊子会贡献P/Q,我们只需要数一数有多少蚊子经过了1号点.这是非常好数的.期望得分10分.
算法4:
对于p/q=1的测试点4,蚊子只要经过被控制的点就必死无疑.我们只需要统计有多少只蚊子至少经过了一个被控制的点.我们以1为根建树,f[i]表示i所在子树内叶节点的个数,对于每一个被控制的点,我们统计以其为lca的路径条数.复杂度O(n). 这部分分对标算是有提示的.
算法5:
考虑对每一条路径进行讨论,求解lca之后算出这条路径经过的被影响的点数.复杂度应当为O(m2logn),但是细节较多,出题人没有写.
可以通过第6,7个测试点,结合前面的算法可以得到70分.
算法6(满分做法):
考虑对算法4进行拓展.此时每只蚊子的贡献是1-(1-p/q)k.我们可以先不管前面那个1,把后面的(1-p/q)k的和求出来,最后用m(m-1)减去那个和就可以了.
一条路径可以在LCA处拆成两条.我们考虑如何求出以某个点为LCA的所有路径的贡献之和.一条路径可以拆成两部分,一部分是从一个叶节点走到LCA,另一部分是从LCA走到另一个叶节点.把这两部分看作两条”半路径”.
记g[i]为以i为LCA的所有半路径的贡献之和.(即:对于i子树内的每个叶节点x,g[i]+=(1-p/q)k,k是x到i的半路径上的被控制点的个数).g[i]可以通过O(n)树形DP得到.
接下来我们通过g[i]求出以每个点为LCA的路径的贡献之和.
对于点x,考虑它的所有儿子,每一对儿子(u,v)的贡献是g[u]
g[v]*(1-p/q)
注意,(u,v)和(v,u)都需要算一次.
直接暴力枚举每一对儿子会超时.我们通过乘法分配律可以O(儿子个数)的时间复杂度算出来.总的时间复杂度仍为O(n).空间复杂度也为O(n).但是常数可能较大,因此出题人给了5s的时限.
算法6:
本题属于树上路径统计问题,可以树分治.复杂度O(nlogn).期望得分50分.
可能有选手通过大力卡常数能够直接用树分治AC此题.

posted @ 2019-10-28 20:58  蓝银杏-SSW  阅读(183)  评论(0编辑  收藏  举报
//结束