纪中国庆10.6做题小结

T1:遥控车

Description
平平带着韵韵来到了游乐园,看到了n辆漂亮的遥控车,每辆车上都有一个唯一的名字name[i]。韵韵早就迫不及待地想玩名字是s的遥控车。可是韵韵毕竟还小,她想象的名字可能是一辆车名字的前缀(也就是说能确定一个i,使s是name[i]的前缀),这时她就能玩第i辆车;或者是一个无中生有的名字,即s不是任何一辆车名字的前缀,这时候她什么也不能玩。
你需要完成下面的任务:
1.韵韵想了m个她想要的名字,请告诉她能玩多少次。
2.由于管理员粗心的操作,导致每辆车的摆放位置都可能出现微小的差错,原来第i辆车现在的位置可能是i-1、i、i+1中的任意一个(第1辆车的位置不可能是0,第n辆车的位置不可能是n+1)。请你计算出共有多少种可能的排列。
注:数据保证当s是name[i]的前缀时,i是唯一确定的。一辆车可以玩多次。

Input
第一行是2个正整数n、m。
接下来n行,每行1个字符串name[i],表示第i辆车的名字。
接下来m行,每行1个字符串s,表示韵韵想要的名字。

Output
第一行输出韵韵能玩的次数。
第二行输出共有多少种可能的排列。

Sample Input

4 4
Abcd
DeF
AAa
aBccc
Ab
AA
AbC
aBcc

Sample Output

3
5

Hint
【数据规模和约定】
对于题目涉及到的字符串严格区分大小写,且长度小于255。
对于20%的数据 n≤10,m≤10;
对于40%的数据 n≤1000,m≤1000;
对于100%的数据 n≤10000,m≤10000。

简要思路:这题其实是两道题硬生生地合成的一道题,总体难度不算高,但会坑人。题目的第一小问一般人(包括我)都会考虑 t r i e trie trie,然而,题目保证前缀唯一确定,证明这些字符串共享同一节点的概率微乎其微,空间复杂度最糟为 52 ∗ 255 ∗ 10000 = 132600000 52 * 255 * 10000 = 132600000 5225510000=132600000,平时开个 25000000 25000000 25000000的空间都提心吊胆了,不用想,绝对会爆。我们可以用万能的 s o r t sort sort排字符串,二分查找即可。至于第二问,设答案为 f ( i ) f(i) f(i),由于最后一个数只能放在 i i i i − 1 i - 1 i1,确定后面的 1 或 2 1或2 12个数可缩小问题的范围,可得 f ( i ) = f ( i − 1 ) + f ( i − 2 ) f(i)=f(i - 1) + f(i - 2) f(i)=f(i1)+f(i2),并且不难得出 f ( 1 ) = 1 f(1) = 1 f(1)=1 f ( 2 ) = 2 f(2) = 2 f(2)=2,可以发现第二题就是斐波那契数列,只是要高精罢了(说得这么轻松 )。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#define N 10005
#define mod 1000000
using namespace std;
int n , m;
int numa[1005] , numb[1005];
string s[N];
string ss;
template < typename T >
inline void read( T & res )  {
	res = 0;
	T pd = 1;
	char aa = getchar();
	while ( aa < '0' || aa > '9' ) {
		if ( aa == '-' ) {
			pd = -pd;
		}
		aa = getchar();
	}
	while ( aa >= '0' && aa <= '9' ) {
		res = ( res << 1 ) + ( res << 3 ) + ( aa - '0' );
		aa = getchar();
	}
	res *= pd;
	return;
}
inline void cal( int a[] , int b[] , int c[] ) {
	int t[1005] = {0};
	int tem[1005] = {0};
	t[0] = max( a[0] , b[0] );
	for ( int i = 1 ; i <= t[0] ; ++i ) {
		t[i] = a[i] + b[i];
	}
	for ( int i = 2 ; i <= t[0] + 1 ; ++i ) {
		t[i] += t[i - 1] / mod;
		t[i - 1] %= mod;
	}
	while ( t[t[0] + 1] > 0 ) {
		t[0]++;
	}
	tem[0] = b[0];
	for ( int i = 1 ; i <= b[0] ; ++i ) {
		tem[i] = b[i];
	}
	memcpy( a , tem , sizeof(tem) );
	memcpy( c , t , sizeof(t) );
	return;
}
int main () {
	read(n);
	read(m);
	for ( int i = 1 ; i <= n ; ++i ) {
		cin >> s[i];
	}
	sort( s + 1 , s + 1 + n );
	int ccnt = 0;
	int l , r;
	for ( int i = 1 ; i <= m ; ++i ) {
		cin >> ss;
		l = 1;
		r = n;
		while ( l < r ) {
			int mid = ( l + r ) >> 1;
			if ( s[mid] >= ss ) {
				r = mid;
			} else {
				l = mid + 1;
			}
		}
		if ( s[r].find( ss , 0 ) == 0 ) {
			ccnt++;
		}
	}
	printf("%d\n",ccnt);
	if ( n <= 2 ) {
		printf("%d",n);
		return 0;
	}
	memset( numa , 0 , sizeof( numa ) );
	memset( numb , 0 , sizeof( numb ) );
	numa[0] = numb[0] = 1;
	numa[1] = 1;
	numb[1] = 2;
	for ( int i = 3 ; i <= n ; ++i ) {
		cal( numa , numb , numb );
	}
	printf("%d",numb[numb[0]]);
	for ( int i = numb[0] - 1 ; i >= 1 ; --i ) {
		printf("%06d",numb[i]);
	}
	return 0;
}

T2:车展

Description
遥控车是在是太漂亮了,韵韵的好朋友都想来参观,所以游乐园决定举办m次车展。车库里共有n辆车,从左到右依次编号为1,2,…,n,每辆车都有一个展台。刚开始每个展台都有一个唯一的高度h[i]。主管已经列好一张单子:
L1 R1
L2 R2

Lm Rm
单子上的(Li,Ri)表示第i次车展将要展出编号从Li到Ri的车。
为了更加美观,展览时需要调整展台的高度,使参展所有展台的高度相等。展台的高度增加或减少1都需花费1秒时间。由于管理员只有一个人,所以只好对每个展台依次操作。每次展览结束后,展台高度自动恢复到初始高度。
请告诉管理员为了举办所有展览,他最少需要花多少时间将展台调整好。

Input
第一行为两个正整数n、m。
第二行共n个非负整数,表示第i辆车展台的高度h[i]。
接下来m行每行2个整数Li、Ri(Li≤Ri)。

Output
一个正整数,调整展台总用时的最小值。

Sample Input

6 4
4 1 2 13 0 9
1 5
2 6
3 4
2 2

Sample Output

48

Hint
【数据规模和约定】
对于50%的数据 n≤500,m≤1000;
对于80%的数据 n≤1000,m≤100000;
对于100%的数据n≤1000,m≤200000;
答案小于2^64。

简要思路:画图可得这题区间的最小操作秒数就是区间的每个数和中位数的绝对值之差,这题其实就是经典的对顶堆算法,一个堆存的数从小到大,另一个堆存的数从大到小,就可以轻松地查询堆中的中位数,同时,另外用两个变量,分别存储选定区间中比中位数大的数的总和以及比中位数小的数的总和,就可以得到区间的的最小操作秒数。预处理区间的操作值,询问时就只要 O ( 1 ) O(1) O(1)处理即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#define ll long long
#define N 1005
using namespace std;
priority_queue< int , vector<int> , greater<int> > qg;
priority_queue< int , vector<int> , less<int> > ql;
int n , m;
ll sumg , suml;
int h[N];
ll tot[N][N];
template < typename T >
inline void read( T & res )  {
	res = 0;
	T pd = 1;
	char aa = getchar();
	while ( aa < '0' || aa > '9' ) {
		if ( aa == '-' ) {
			pd = -pd;
		}
		aa = getchar();
	}
	while ( aa >= '0' && aa <= '9' ) {
		res = ( res << 1 ) + ( res << 3 ) + ( aa - '0' );
		aa = getchar();
	}
	res *= pd;
	return;
}
int main () {
	read(n);
	read(m);
	for ( int i = 1 ; i <= n ; ++i ) {
		read(h[i]); 
	}
	for ( int i = 1 ; i <= n ; ++i ) {
		while( ql.size() ) {
			ql.pop();
		}
		while ( qg.size() ) {
			qg.pop();
		}
		sumg = suml = 0;
		for ( int j = i ; j <= n ; ++j ) {
			if ( ql.size() == qg.size() ) {
				ql.push(h[j]);
				suml += h[j];
			} else {
				qg.push(h[j]);
				sumg += h[j];
			}
			if ( ql.size() && qg.size() ) {
				int left = ql.top();
				int right = qg.top();
				if ( left > right ) {
					ql.pop();
					qg.pop();
					suml += right - left;
					sumg += left - right;
					ql.push(right);
					qg.push(left); 
				}
			}
			int mid = ql.top();
			int cntl = ql.size();
			int cntg = qg.size();
			tot[i][j] = ( cntl * mid - suml ) + ( sumg - cntg * mid );
		}
	}
	int x , y;
	ll ans = 0;
	for ( int i = 1 ; i <= m ; ++i ) {
		read(x);
		read(y);
		ans += tot[x][y];
	}
	printf("%lld",ans);
	return 0;
}

T3:拉力赛

Description
车展结束后,游乐园决定举办一次盛大的山道拉力赛,平平和韵韵自然也要来参加大赛。
赛场上共有n个连通的计时点,n-1条赛道(构成了一棵树)。每个计时点的高度都不相同(父结点的高度必然大于子结点),相邻计时点间由赛道相连。由于马力不够,所以韵韵的遥控车只能从高处驶向低处。而且韵韵的车跑完每条赛道都需花费一定的时间。
举办方共拟举办m个赛段的比赛,每次从第u个计时点到第v个计时点,当然其中有不少比赛韵韵的遥控车是不能参加的(因为要上坡)。平平想知道他能参加多少个赛段的比赛,并且想知道他完成这些赛段的总用时。

Input
第一行两个整数n,m。
接下来n-1行每行3个整数a、b、t。
表示韵韵的遥控车可以花t秒从第a个计时点到第b个计时点。
接下来m行每行2个整数u、v,意义如描述所示。

Output
第一行输出一个正整数,表示能参加的赛段数。
第二行输出一个正整数,表示总用时。

Sample Input

6 2
1 2 1
2 4 1
2 5 1
5 6 1
1 3 1
2 6
4 5

Sample Output

1
2

Hint
【数据规模和约定】
第一个计时点的高度是最高的;
u≠v;
对于50%的数据 n≤1000 m≤1000;
对于100%的数据 n≤10000 m≤100000;
答案小于2^64。

简要思路:这题肯定是最简单的,只要用 l c a lca lca并存边权就可以秒过,不过,要注意这里 u u u v v v不可交换,如果 u u u的深度大于 v v v,那么平平就不可以参赛,如果发现 u u u v v v不在同一条树链上,那么平平也不可以参赛。

#include <iostream>
#include <cstdio>
#include <cstring>
#define N 10005
#define ll long long
using namespace std;
int n , m , bcnt;
ll tot;
int f[N][15] , loger[N] , dep[N];
ll g[N][15];
int head[N];
struct node{
	int next;
	int to;
	ll val;
}str[N];
template < typename T >
inline void read( T & res )  {
	res = 0;
	T pd = 1;
	char aa = getchar();
	while ( aa < '0' || aa > '9' ) {
		if ( aa == '-' ) {
			pd = -pd;
		}
		aa = getchar();
	}
	while ( aa >= '0' && aa <= '9' ) {
		res = ( res << 1 ) + ( res << 3 ) + ( aa - '0' );
		aa = getchar();
	}
	res *= pd;
	return;
}
inline void insert( int from , int to , ll val ) {
	str[++bcnt].next = head[from];
	head[from] = bcnt;
	str[bcnt].to = to;
	str[bcnt].val = val;
	return;
}
inline void dfs( int cur , int fa ) {
	f[cur][0] = fa;
	dep[cur] = dep[fa] + 1;
	for ( int i = 1 ; i <= loger[dep[cur]] + 1 ; ++i ) {
		f[cur][i] = f[f[cur][i - 1]][i - 1];
		g[cur][i] = g[f[cur][i - 1]][i - 1] + g[cur][i - 1];
	}
	for ( int i = head[cur] ; i ; i = str[i].next ) {
		int sn = str[i].to;
		g[sn][0] = str[i].val;
		dfs( sn , cur );
	}
	return;
}
inline bool lca( int u , int v ) {
	tot = 0;
	if ( dep[u] > dep[v] ) {
		return 0;
	}
	for ( int i = loger[dep[v]] ; i >= 0 ; --i ) {
		if ( dep[f[v][i]] >= dep[u] ) {
			tot += g[v][i];
			v = f[v][i];
		}
	}
	if ( u != v ) {
		return 0;
	}
	return 1;
}
int main() {
	read(n);
	read(m);
	loger[0] = -1;
	loger[1] = 0;
	for( int i = 2 ; i <= n ; ++i ) {
		loger[i] = loger[i >> 1] + 1;
	}
	bcnt = 0;
	int a , b;
	ll t;
	for ( int i = 1 ; i <= n - 1 ; ++i ) {
		read(a);
		read(b);
		read(t);
		insert( a , b , t );
	}
	dfs( 1 , 0 );
	int u , v;
	ll ans = 0;
	int ccnt = 0;
	for ( int i = 1 ; i <= m ; ++i ) {
		read(u);
		read(v);
		if ( !lca( u , v ) ) {
			continue;
		} else {
			ans += tot;
			ccnt++;
		}
	}
	printf("%d\n%lld",ccnt,ans);
	return 0;
}

T4:舞台设置

Description
游乐园决定在一个n×m的广场上举办一次颁奖晚会,总管要你帮忙搭建一个舞台。
现在给你广场的布置图(规定地图的上方为正北),有些位置需要布置为观众席(记为1),另一些是空地(记为0)。舞台只能在空地上搭建。
为了使晚会更加吸引人,平平觉得舞台应该是朝北的h—金字塔形。h—金字塔形舞台是由h个矩形舞台相接而成的,其中后方的矩形舞台的两端必须超出在其前面的矩形舞台,且最小矩形面对的朝向为舞台的方向。下面给出几个实例:
图1
舞台的面积应该尽量大,输出面积最大的朝北h—金字塔形舞台的面积。

Input
第一行3个整数 n、m、h。
接下来n行,每行m个0或1,中间用一个空格隔开。

Output
一个整数,表示最大的朝北的h—金字塔形舞台的面积。
如果没有符合题意的h—金字塔形舞台输出0。

Sample Input

4 6 2
0 0 1 0 0 1
0 0 0 0 0 0
0 0 1 0 0 0
0 1 1 0 0 0

Sample Output

10

Hint
样例对应的最优方案如下图:
图2
×表示观众席
【数据规模和约定】
对于10%的数据 h=1;
对于40%的数据 h≤5;
对于100%的数据 h≤20;
对于100%的数据 n、m≤100。

简要思路:这题就是一道不太好想的 d p dp dp,我们用 d p [ i ] [ x ] [ y ] [ k ] dp[i][x][y][k] dp[i][x][y][k]表示第 i i i层,以 x x x y y y为底边并且已经有 k k k个矩形的最大方案数, f [ i ] [ x ] [ y ] [ k ] f[i][x][y][k] f[i][x][y][k]表示第 i i i层,底边范围在 x x x y y y以内(不含 x x x y y y)并且已经有 k k k个矩形的最大方案数。
d p dp dp方程为:
f [ i ] [ x ] [ y ] [ k ] = m a x ( d p [ i ] [ x + 1 ] [ y − 1 ] [ k ] , f [ i ] [ x + 1 ] [ y ] [ k ] , f [ i ] [ x ] [ y − 1 ] [ k ] ) f[i][x][y][k] = max( dp[i][x + 1][y - 1][k] , f[i][x + 1][y][k] , f[i][x][y - 1][k] ) f[i][x][y][k]=max(dp[i][x+1][y1][k],f[i][x+1][y][k],f[i][x][y1][k])
d p [ i ] [ x ] [ y ] [ k ] = m a x ( d p [ i − 1 ] [ x ] [ y ] [ k ] , f [ i − 1 ] [ x ] [ y ] [ k − 1 ] ) + y − x + 1 dp[i][x][y][k] = max( dp[i -1][x][y][k] , f[i-1][x][y][k - 1] ) + y - x + 1 dp[i][x][y][k]=max(dp[i1][x][y][k],f[i1][x][y][k1])+yx+1
如果 x x x y y y之间有零(用前缀和判断),则 d p [ i ] [ x ] [ y ] [ k ] = 0 dp[i][x][y][k] = 0 dp[i][x][y][k]=0
由于当前行的值只会受到上一行的影响,所以我们可以考虑滚动行数,节约空间。
同时为了顾及到 d p dp dp的后效性,对于 x x x y y y,采用 x x x m m m 1 1 1 y y y x x x m m m的方式枚举(可画图理解)。
答案的统计要在 d p dp dp中,当 k = h k=h k=h时就更新一次答案。

#include <iostream>
#include <cstdio>
#include <cstring>
#define N 105
#define H 25
using namespace std;
int n , m , h , pos;
int sum[N][N];
int f[2][N][N][H];
int dp[2][N][N][H];
template < typename T >
inline void read( T & res )  {
	res = 0;
	T pd = 1;
	char aa = getchar();
	while ( aa < '0' || aa > '9' ) {
		if ( aa == '-' ) {
			pd = -pd;
		}
		aa = getchar();
	}
	while ( aa >= '0' && aa <= '9' ) {
		res = ( res << 1 ) + ( res << 3 ) + ( aa - '0' );
		aa = getchar();
	}
	res *= pd;
	return;
}
int main () {
	read(n);
	read(m);
	read(h);
	pos = 0;
	int tem;
	for ( int i = 1 ; i <= n ; ++i ) {
		for ( int j = 1 ; j <= m ; ++j ) {
			read(tem);
			sum[i][j] = sum[i][j - 1] + tem;
		}
	}
	int ans = 0;
	for ( int tim = 1 ; tim <= n ; ++tim ) {
		for ( int k = 1 ; k <= h ; ++k ) {
			if ( k > tim ) {
				continue;
			}
			for ( int i = m ; i >= 1 ; --i ) {
				for ( int j = i ; j <= m ; ++j ) {
					if ( j - i + 1 >= 3 ) {
						f[pos][i][j][k] = max( dp[pos][i + 1][j - 1][k] , max( f[pos][i + 1][j][k] , f[pos][i][j - 1][k] ) );
					}
					if ( sum[tim][j] - sum[tim][i - 1] == 0 && ( k == 1 || f[pos ^ 1][i][j][k - 1] > 0 ) ) {
						dp[pos][i][j][k] = max( dp[pos ^ 1][i][j][k] , f[pos ^ 1][i][j][k - 1] ) + j - i + 1;
						if ( k == h ) {
							ans = max( ans , dp[pos][i][j][k] );
						}
					} else {
						dp[pos][i][j][k] = 0;
					}	
				}
			}
		}
		pos ^= 1;
	}
	printf("%d",ans);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值