题目
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 n≤100000,每个数的权值都是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 n≤100000,m≤10000,每堆的石子数<=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(ai≤aj)
那么那个最大值怎么维护了,可以用权值线段树,其中下标为
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;
}