2019.07.09【NOIP提高组】模拟 A 组 比赛总结

这篇博客回顾了2019年NOIP提高组模拟赛的比赛情况,分析了三道题目:wyl8899的TLE,法法塔的奖励和wyl8899与法法塔的游戏。博主提到在T1题目中错误地使用了复杂算法,而T2题目则采用了错误的思路。博主分享了每道题目的解题思路,包括暴力解法和正解,并反思了自己的比赛策略,强调了暴力解法的重要性。
摘要由CSDN通过智能技术生成

题目

wyl8899的TLE

Time Limits: 1000 ms
Memory Limits: 262144 KB

Description

wyl8899今天也很刻苦的在做老师布置下来的题目!

这一天老师布置的题目是这样的:

给出两个仅含小写字母的字符串A和B,输出最大的k,使得A[1…k]是B的子串。

A和B的长度都不会超过50000。

很显然他并不知道正确的做法,但是他居然卡着时间过掉了老师给的数据!

你找到了他提交给老师的程序,经过测试你惊讶的发现,他的程序运行时间恰好是最终答案,单位是毫秒。

你现在找到了老师给的数据中的一笔,你决定至多修改A串中的一个字母,使得他的程序尽可能的慢。

现在,你能告诉我修改数据之后他的程序在这个测试点的运行时间么?(单位当然还是毫秒)

Input

两行,每行一个仅含小写字母的字符串,分别是A和B。
保证A和B的长度都不超过50000。

Output

你修改数据之后,wyl8899的程序在这个测试点的运行时间,单位是毫秒。

Sample Input

输入1:
adbc
aabbabd
输入2:
aab
aabcc

Sample Output

输出1:
3
输出2:
3

Data Constraint

保证A和B的长度都不超过50000。

Hint

数据是在Windows下生成的,测评环境是Linux。

由于换行符的差异,对于C/C++选手,请不要使用gets(s)读入一行字符,建议使用scanf(“%s”,s)的形式。

Pascal选手直接使用readln()读入一行即可,理论上readln()不会受到不同换行符的影响。


法法塔的奖励

Time Limits: 1000 ms
Memory Limits: 524288 KB

Description

法法塔是很喜欢写程序的。所以冒着就算码农屌丝一辈子的风险也大无畏地写着程序。
码农们为了表彰法法塔的坚持与执着,决定给法法塔颁布奖励,为了体现码农的屌丝气质,奖励也将由法法塔自己做出选择!
所有的奖励被组织成了一棵树的形态,每个点都有一个权值。法法塔首先选择一个子树,然后选择从该子树内的一个叶子节点到该子树的根的一条路径,将路径上节点的权值依次排成一个序列,求得这个序列的最长不下降子序列长度,这个长度就是他能得到的奖品数目。要求该子树的根必须被选择。
接下来就是法法塔进行选择的时间了。尽可能多地得到奖品是自然的。那么法法塔到底能够得到多少奖品呢?这是个很纠结的问题,他决定交给你来解决。对于每个子树,他都希望你能求出他首先选择了这个子树后,他能得到的最多的奖品数目。

Input

第一行一个数n,描述树上节点的个数。
接下来一行n个数,描述树上每个点的父亲,点1为根,其父亲设为-1,其余每个点的父亲的标号都小于自己。
接下来一行n个数,表示树上每个点的权值。

Output

一行n个数,第i个数表示法法塔选择了以第i个节点为根的子树后,他可以获得的最多的奖品个数。

Sample Input

输入1:
2
-1 1
1 1
输入2:
6
-1 1 2 1 2 4
4 3 4 3 6 2

Sample Output

输出1:
2 1
输出2:
3 1 1 2 1 1

Data Constraint

n ≤ 100000 n\leq 100000 n100000,每个数的权值都是1到n的正整数。


wyl8899和法法塔的游戏

Time Limits: 3000 ms
Memory Limits: 524288 KB

Description

法法塔和wyl8899都喜欢玩游戏。但是每次玩游戏法法塔都被wyl8899虐。

为了安慰可怜的法法塔,wyl8899决定大发慈悲,修改了一下游戏规则。

是这样的,这儿有一堆石子排成一列,每次wyl8899让hza选择一个区间进行游戏。游戏嘛,就是采用最普通的规则:两人轮流操作,每次选择一堆石子,取掉不为0的石子数,没法操作者失败。法法塔要做的是这样的:

我们现在定义一个区间的弱菜值:表示如果法法塔先取石子,而且法法塔第一步取的石子规定是最右边的那堆的话,为了必胜,第一步需要取的石子数。如果没法获胜的话,那么弱菜值就是-1。

法法塔要选择一个区间[l,r],使得r为wyl8899给定的某个数,l属于[a,b],a,b也是wyl8899给定的,要求这个区间的弱菜值是满足前面的条件中最大。请给出这个最大的弱菜值。

如果最大弱菜值不为-1,法法塔就会马上取走该区间最右端的石子堆最大弱菜值数量的石子。

Input

第一行一个正整数n,描述了石子的堆数,接下来一行n个数,描述每堆石子的数目。
接下来一行m,表示将进行要进行m次游戏,每次游戏由三个值刻画,r,a,b。r表示被选定的右端点,[a,b]表示选择的区间的左端点的范围

Output

对于每个询问输出最大的弱菜值。

Sample Input

输入1:
12
662 477 125 483 17 560 232 59 176 928 807 659
5
5 2 5
4 4 4
2 1 2
6 4 6
6 2 3
输入2:
7
230 883 456 1020 966 899 610
2
7 1 2
2 1 2
输入3:
11
392 808 14 71 393 79 11 74 713 232 142
5
8 3 8
9 5 9
3 1 1
8 2 8
7 4 6

Sample Output

输出1:
17
483
477
560
-1
输出2:
352
883
输出3:
74
713
-1
-1
-1

Data Constraint

n ≤ 100000 , m ≤ 10000 n\leq 100000,m\leq 10000 n100000,m10000,每堆的石子数<=1000。


总结

今天的题目不算太难,暴力的分数是很可观的,然而我
在这里插入图片描述
Emmmmmmm,我比赛的策略出了问题!
首先,我看到T1,觉得是后缀数组或是扩展KMP等神犇算法,就跳过了。
看了T2,我不知怎样地冒出了一个奇怪的念头:这题可以把最长不下降子序列倒过来,从根向儿子做最长不上升子序列
然后就GG了。
比赛过程中,我用了半个小时读题,再用1个多小时回忆最长不下降子序列 O ( n log ⁡ 2 n ) O(n\log_2n) O(nlog2n)算法并证明它的正确性。
接着思路跟不上了,发现我的做法有问题,于是苦思冥想了30分钟,比赛还有1个小时左右就要结束了,于是急急忙忙地打水法(即我一开始所以为的正解),再打T1暴力,不料把“修改一个字符”看作了“删除一个字符”了……
于是愉快地爆了〇。

其实今天的比赛是名副其实的数据水,T1和T3暴力、水法碾压正解!
然而我却错误地选择了打正解、并且错误地选择了T2
满脸黑线。。。

第一题正解有很多,我认为的2个神犇算法便在其中,当然还有二分+字符串哈希。可以枚举B串的一个起点 i i i,二分出A串中的最长匹配位置 j j j,那么最优解显然是把 j + 1 j+1 j+1修改了,于是再从 i + j + 1 i+j+1 i+j+1继续匹配,把2段的答案加起来。

第二题可以设 f i f_i fi表示根为 i i i的子树答案。于是有状态转移方程:
f i = 1 + max ⁡ j 在以 i 为根的子树内 f j ( a i ≤ a j ) f_i=1+\max_{j在以i为根的子树内}f_j\quad(a_i\leq a_j) fi=1+j在以i为根的子树内maxfj(aiaj)
那么那个最大值怎么维护了,可以用权值线段树,其中下标为 a a a,权值是 f f f
这里涉及到线段树的合并,这个类似于主席树,使点数不至于太多。
P.S:这题居然把指针给卡了!

第三题暴力。
可以参考nim游戏,当几堆石子的异或和为0时,先手必败;否则存在最优解。
O ( n 2 ) O(n^2) O(n2)就可以过了(没有吸氧、臭氧哦!)。
感悟:暴力还是要打的,万一AC了呢?


CODE

T1

#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
#define mod 998244353
#define N 50005
#define prime 29
char a[N],b[N];
ll n,m,suma[N],sumb[N],pow[N];
inline ll get1(ll l,ll r){return (suma[r]+mod-suma[l-1]*pow[r-l+1]%mod)%mod;}
inline ll get2(ll l,ll r){return (sumb[r]+mod-sumb[l-1]*pow[r-l+1]%mod)%mod;}
inline ll match(ll l1,ll l2)
{
	if(l1>n||l2>m) return 0;
	ll mid,l=1,r=n-l1<m-l2?n-l1+1:m-l2+1,s=0;
	while(l<=r)
	{
		mid=l+r>>1;
		if(get1(l1,l1+mid-1)==get2(l2,l2+mid-1)) l=mid+1,s=mid;
		else r=mid-1;
	}
	return s;
}
int main()
{
	ll i,j,k,ans=0,max,s;
	scanf("%s%s",a+1,b+1);
	n=strlen(a+1),m=strlen(b+1),max=n>m?n:m,pow[0]=1;
	for(i=1;i<=n;i++) suma[i]=(suma[i-1]*prime%mod+a[i]-'a')%mod;
	for(i=1;i<=m;i++) sumb[i]=(sumb[i-1]*prime%mod+b[i]-'a')%mod;
	for(i=1;i<=max+3;i++) pow[i]=pow[i-1]*prime%mod;
	for(i=1;i<=m;i++)
	{
		j=match(1,i);
		if(j==n||i+j-1==m){if(ans<j) ans=j;break;}
		k=match(j+2,i+j+1);
		if(j+k+1>ans) ans=j+k+1;
	}
	printf("%lld\n",ans);
	return 0;
}

T2

#include<cstdio>
using namespace std;
#define N 100005
struct node
{
	int max,lson,rson;
	node(){max=lson=rson=0;}
}set[2000005];
int root[N],fir[N],nex[N],ans[N],a[N],n,cnt;
inline int mymax(int x,int y){return x>y?x:y;}
void add(int &k,int l,int r,int id,int num)
{
	if(!k) k=++cnt;
	if(set[k].max<num) set[k].max=num;
	if(l==r) return;
	int mid=l+r>>1;
	if(id<=mid) add(set[k].lson,l,mid,id,num);
	else add(set[k].rson,mid+1,r,id,num);
}
int merge(int x,int y)
{
	if(!x) return y;if(!y) return x;
	if(set[x].max<set[y].max) set[x].max=set[y].max;
	set[x].lson=merge(set[x].lson,set[y].lson);
	set[x].rson=merge(set[x].rson,set[y].rson);
	return x;
}
int qry(int k,int l,int r,int x,int y)
{
	if(!k) return 0;
	if(l==x&&r==y) return set[k].max;
	int mid=l+r>>1;
	if(y<=mid) return qry(set[k].lson,l,mid,x,y);
	if(x>mid) return qry(set[k].rson,mid+1,r,x,y);
	return mymax(qry(set[k].lson,l,mid,x,mid),qry(set[k].rson,mid+1,r,mid+1,y));
}
void dfs(int k)
{
	for(int i=fir[k];i;i=nex[i])
		dfs(i),root[k]=merge(root[k],root[i]);
	ans[k]=qry(root[k],1,n,1,a[k])+1;
	add(root[k],1,n,a[k],ans[k]);
}
int main()
{
	int i,j;
	scanf("%d%d",&n,&j);
	for(i=2;i<=n;i++) scanf("%d",&j),nex[i]=fir[j],fir[j]=i;
	for(i=1;i<=n;i++) scanf("%d",a+i);
	dfs(1);
	for(i=1;i<=n;i++) printf("%d ",ans[i]);
	return 0;
}

T3

#include<cstdio>
using namespace std;
#define N 100005
int s[N];
int main()
{
	int n,m,i,a,b,k,sum,ans;
	scanf("%d",&n);
	for(i=1;i<=n;i++) scanf("%d",s+i);
	scanf("%d",&m);
	while(m--)
	{
		scanf("%d%d%d",&k,&a,&b);
		if(b==k)
		{
			if(s[k]) printf("%d\n",s[k]),s[k]=0;
			else puts("-1");continue;
		}
		for(i=k-1,sum=0;i>b;i--) sum^=s[i];
		for(ans=0;i>=a;i--)
		{
			sum^=s[i];
			if(ans<s[k]-sum) ans=s[k]-sum;
			if(ans==s[k]) break;
		}
		if(ans) s[k]-=ans,printf("%d\n",ans);
		else puts("-1");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值