最近的比赛总结

7.28【NOIP提高组】模拟

赛时

T1:一眼看字符串,果断模拟水40滚粗。
T2:一眼看字符串还加个环,果断找方案后n方模拟46分滚粗
T3:一看在这里插入图片描述后知道不可做,果断放弃滚粗,赛后发现dp能水65分(本蒟蒟瑟瑟发抖)。
T4:一眼看发觉dp有后效性,推了2个小时的方程后暴力滚粗(14分)
40+46+14=100分,12/50
勉强挤进前12(说的真勉强)

总结

T1:Description

文本编辑器的一个最重要的机能就是复制&粘贴。JOI社现在正在开发一款能够非常高速地进行复制&粘贴的文本编辑器,作为JOI社一名优秀的程序猿,你担负起了复制&粘贴功能的测试这一核心工作。整个JOI社的命运都系在你的身上,因此你无论如何都想写出一个正确且高速的程序来完成这项工作。
具体的做法如下所示。文件的内容是一个字符串S,对其进行N次复制&粘贴的操作,第i次操作复制位置Ai和位置Bi之间的所有文字,然后在位置Ci粘贴。这里位置x表示字符串的第x个字符的后面那个位置(位置0表示字符串的开头),例如字符串”copypaste”的位置6表示字符’a’和字符’s’之间的位置,位置9表示’e’后面的位置(即字符串的结尾)。不过,如果操作后的字符串长度超过了M,那么将超过的部分删除,只保留长度为M的前缀。
你的任务是写一个程序,输出N次操作后字符串的前K个字符。

Input

第一行两个空格分隔的正整数K和M,表示最终输出的文字数量和字符串长度的上限。
第二行一个字符串S,表示初始的字符串。
第三行一个整数N,表示操作的次数。
接下来N行,第i行包含三个空格分隔的整数Ai,Bi,Ci,表示第i次操作将位置Ai到Bi之间的字符串复制,然后粘贴到位置Ci。

Output

输出一行一个长度为K的字符串,表示最终的字符串的长度为K的前缀

Sample Input

2 18

copypaste

4
3 6 8
1 5 2
4 12 1
17 18 0

Sample Output

ac
【HINT】
初始的字符串为”copypaste”。
第一次操作将从位置3到位置6的字符串”ypa”复制,插入位置8,得到字符串”copypastypae”
第二次操作将从位置1到位置5的字符串”opyp”复制,插入位置2,得到字符串”coopyppypastypae”
第三次操作将从位置4到位置12的字符串”yppypast”复制,插入位置1,得到字符串”cyppypastoopyppypastypae”,由于长度超过了M=18,删除超过的部分,得到”cyppypastoopyppypa”
第四次操作将从位置17到位置18的字符串”a”复制,插入位置0,得到字符串”acyppypastoopyppypa”,删除超过18的部分得到”acyppypastoopyppyp”
最后输出长度为2的前缀”ac”即为答案。

Data Constraint

对于40%的数据,N,M<=2000
对于100%的数据:
1<=K<=200
1<=M<=10^9
S的每个字符都是小写字母(‘a’~’z’)
K<=|S|<=min(M,210^5)
1<=N<=2
10^5
设第i次操作前的字符串长度为Li,那么0<=Ai<Bi<=Li且0<=Ci<=Li (1<=i<=N)

正解

由于k十分的小,所以我们将操作输入后,考虑递归第i位找到是什么(i<=k)

#include<cstdio>
#define N 200007
using namespace std;
int k,n,m,x[N],y[N],z[N];
char s[1000000];
char find(int num,int k){
	if(!num) return s[k];
	if(k<z[num]) return find(num-1,k);
	else if(k>=z[num]&&k<z[num]+y[num]-x[num]) return find(num-1,k+x[num]-z[num]);
	else return find(num-1,k+x[num]-y[num]); 
}
int main(){
	freopen("copypaste.in","r",stdin);
	freopen("copypaste.out","w",stdout);
	scanf("%d%d",&k,&m);
	scanf("%s",s);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d%d%d",&x[i],&y[i],&z[i]);
	for(int i=0;i<k;i++)
		printf("%c",find(n,i));
}

T2:Description

K理事长正在思考日本信息学奥林匹克竞赛选手的应援道具的logo问题。某天,K理事长突发奇想,想要设计一个用’J’,’O’,’I’三种文字环形排列的logo,意为希望选手能从JOI中收获快乐的意思。
(注:“环形地”在日文中的表述为“円状に”,“円”读作“en”,再加上“JOI”三个字即为“enjoy”……)
如下所示,对于任意非负整数k,我们定义标号为k的JOI序列Sk为:
·S0为’J’,’O’,’I’中任一字符构成的长度为1的字符串
·S[k+1]为最初4k个字符都是’J’,接下来的4k个字符都是’O’,接下来的4k个字符都是’I’,最后4k个字符是字符串Sk的长为4^(k+1)的字符串
现在,K理事长在纸上写下了由4^K个文字构成的一个环形字符串,字符串中每个字符都是’J’,’O’,’I’中的一个。K理事长想要修改一些文字,使得得到的字符串从某个起点开始顺时针读一圈后可以得到SK。在满足条件的情况下,要求修改的文字数量最少。

Input

第一行一个正整数K,表示K理事长在纸上写下了一个长度为4^K的环状字符串。
第二行一个由’J’,’O’,’I’三个字符构成的长为4^K的字符串,表示纸上的环形字符串从某个起点出发顺时针阅读一圈得到的字符串。

Output

输出一行一个整数,表示修改文字数量的最小值。

Sample Input

2
JJOIJJOJOIOJOOOI

Sample Output

7

Data Constraint

对于30%的数据,1<=K<=5
对于100%的数据,1<=K<=10

正解

对于每坨相同的字母,我们枚举每一位的时候只需判断首和尾就行了,暴力。
(不想贴码,太丑了)

T3:Description

那一年的七月,最后的夏天,止不住的怀念
西湖畔,傍晚微风中你的笑颜,渐行渐远
一起走过的夏天,一起许下的心愿
十年之后,是否已然忘却
时光再美,怎如初见
……

学车中学的宿舍,到机房有一段小路,路边有N簇野花,第n簇有在这里插入图片描述

朵花。( 表示不大于x的最大整数)
现在,Lyra想取走K朵花,并且要求不能有两朵花出自同一簇花,请问她有多少种取法呢。
她只想知道答案除以M的余数。

目前水了65分,正解是分段(由于a<=3以e为底的log也不大)之后拉格朗日插值法(网上都没题解QWQ)。

T4:Description

职业经营家庭菜园的JOI君每年在自家的田地中种植一种叫做IOI草的植物。IOI草的种子在冬天被播下,春天会发芽并生长至一个固定的高度。到了秋天,一些IOI草会结出美丽的果实,并被收获,其他的IOI草则会在冬天枯萎。
JOI君的田地沿东西方向被划分为N个区域,从西侧开始的第i个区域中种植着IOI草i。在第i个区域种植的IOI草,在春天的时候高度会生长至Hi,此后便不再生长。如果IOI草i会结出果实,那么将会获得Pi的收益,否则没有收益。
春天到了,查看田地样子的JOI君决定拔掉一些种植的IOI草,使利益最大化。拔掉IOI草i需要Ci的花销,拔掉的IOI草会立刻枯萎。IOI草只能在春天被拔掉,夏天和秋天不能拔掉IOI草。
IOI草是一种非常依靠阳光的植物,如果在夏天某个区域的IOI草的东侧和西侧都有比它高的IOI草存在,那么这株IOI草在秋天便不会结出果实。换句话说,为了让没有被拔掉的IOI草i在秋天结出果实,到了夏天的时候,以下两个条件至少满足一个:
1.对于任意1<=j<=i-1,Hj<=Hi或IOI草j已经被拔除
2.对于任意i+1<=j<=N,Hj<=Hi或IOI草j已经被拔除
用最终收获的果实的总价格减掉拔除IOI草的花销的总和,即为JOI君的收益。那么JOI君能从IOI草中获取的最大利益到底有多少呢?

Input

第一行一个正整数N,表示田地被分为了N个区域。
接下来N行,第i行(1<=i<=N)三个空白分割的正整数Hi,Pi,Ci,表示第i株IOI草在春天时高度会生长至Hi,秋天收获的果实的价格为Pi,拔除所需费用为Ci。

Output

输出一行一个整数,表示JOI君能获得的最大利益

Sample Input

7
22 60 30
46 40 30
36 100 50
11 140 120
38 120 20
24 90 60
53 50 20

Sample Output

320
IOI草1、3、5、6的果实价格分别为60、100、120、90,拔除IOI草2和IOI草7的花销分别为30、20,总收益为320,这是所有方案中的最大值。

Data Constraint

对于30%的数据,N<=20
对于45%的数据,N<=300
对于60%的数据,N<=5000
对于100%的数据:
3<=N<=10^5
1<=Hi<=10^9 (1<=i<=N)
1<=Pi<=10^9 (1<=i<=N)
1<=Ci<=10^9 (1<=i<=N)

正解:

设状态 f[i]表示 i 这个 IOI 草前面没有比它更高的草时,1-i 这
些草的收益 (以下的收益都是指减去花费之后的纯收益));
转移为f[i]=max(f[j]-cost[k])
j<k<i且h[k]>h[i],为了方便设一颗比收益
草都低的 0 号草,f[0]=0;
右面按照同样的方法处理,那么答案就是以某个草及其左面草的
收益,和它严格右侧草的收益了(严格右侧是防止其价值被累加两遍);
这个东西似乎是 O(n3)的,我们需要一些优化;
对于所有草的高度做一个离散化,然后对高度开一个线段树,记
录某个高度在这之前的 
j<k<i且h[k]>h[i]
f[j]- cost[k]的最大值;
维护的时候就是将线段树中比它小或等于的部分的值全部减去
cost[i],然后将当前草的 f[i]插入线段树的 h[i]处;
每次查询比它的 h[i]小或等于的最大值就可以了;
时间复杂度 O(nlogn);

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define ll long long
#define N 200007
using namespace std;
int n,tot;
ll g[N],f[N],h[N],p[N],c[N],tr[4*N],lazy[4*N];
struct node{
	int  num,w;
}a[N];
bool cmp(node a,node b){return a.w<b.w;}
void doit(int num,ll x){lazy[num]+=x,tr[num]-=x;}
void down(int num){
	if(lazy[num]){
		doit(num<<1,lazy[num]),doit(num<<1|1,lazy[num]);
		lazy[num]=0;
	}
}
void change(int num,int l,int r,int x,int y,ll z){
	if(l>=x&&r<=y) doit(num,z);
	else{
		down(num);
		int mid=l+r>>1;
		if(x<=mid) change(num<<1,l,mid,x,y,z);
		if(y>mid) change(num<<1|1,mid+1,r,x,y,z);
		tr[num]=max(tr[num<<1],tr[num<<1|1]);
	}
}
void insert(int num,int l,int r,int k,ll x){
	down(num);
	if(l==r) tr[num]=max(tr[num],x);
	else{
		int mid=l+r>>1;
		if(k<=mid) insert(num<<1,l,mid,k,x);
		else insert(num<<1|1,mid+1,r,k,x);
		tr[num]=max(tr[num<<1],tr[num<<1|1]);
	}
}
ll query(int num,int l,int r,int x,int y){
	down(num);
	if(l>=x&&r<=y) return tr[num];
	int mid=l+r>>1;
	if(y<=mid) return query(num<<1,l,mid,x,y);
	else if(x>mid) return query(num<<1|1,mid+1,r,x,y);
	else return max(query(num<<1,l,mid,x,y),query(num<<1|1,mid+1,r,x,y));
}
int main(){
	freopen("herbary.in","r",stdin);
	freopen("herbary.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld%lld%lld",&a[i].w,&p[i],&c[i]),a[i].num=i;
	sort(a+1,a+n+1,cmp);
	a[0].w=-1;
	for(int i=1;i<=n;i++){
		if(a[i].w!=a[i-1].w) tot++;
		h[a[i].num]=tot;
	}
	memset(tr,128,sizeof(tr));
	insert(1,0,tot,0,0);
	for(int i=1;i<=n;i++){
		f[i]=query(1,0,tot,0,h[i])+p[i];
		change(1,0,tot,0,h[i],c[i]),insert(1,0,tot,h[i],f[i]);
	}
	memset(tr,128,sizeof(tr));
	insert(1,0,tot,0,0);
	for(int i=n;i>=1;i--){
		g[i]=query(1,0,tot,0,h[i])+p[i];
		change(1,0,tot,0,h[i],c[i]),insert(1,0,tot,h[i],g[i]);
	}
	ll ans=0;
	for(int i=1;i<=n;i++)
		ans=max(ans,f[i]+g[i]-p[i]);
	printf("%lld",ans);
}

7.29【NOIP提高组】模拟

赛时

T1:一看二分然后眼切之后与旁边大佬对拍(互相)最后10被暴扣QWQ
T2:不管T3T4了直接莽T2找区间之后算答案之后前缀和之后统计答案之后剩最后30分钟补个离散化想着200拿个大众分就很香然后现实就是很残酷就如同你看这段话时没有标点一样让人不知所措。
T3:这个nt 博主是不是把话在T2都讲完了,T4:是的而且似乎考试时它没鸟我们哥俩。T3,T4:所以这个家伙就差点暴0了(10分)
事情经过就是如此,赛后听说T3找个规(da)律(biao)就知道如何如何求了。

总结

T1:爬山

在这里插入图片描述
这个张东升 嗯哼嗯哪……这个球很,很sexy (T3,T4:所以我们一起去爬山吧)
二分就行了。

T2学数数

在这里插入图片描述
输入输出啥的不发了。

正解

这个题我们考虑对于每个值,最大值为它的区间有多少个,也就是这个值的贡献是多少,那么考虑询问就是个前缀和就搞定了。之后我们可以用个队列找到每个值以它为最大值的最大区间的的最左与最右(这个队列的方法跑的飞快),然后我们知道了这个最大区间,那么这个值的贡献自己算就好了,我就随便讲讲自己的方法。对于一个区间,子区间的长度范围为1到这个大区间的长度,我们以大区间的位置i为列,子区间长度为行可以构造出一个区间长度*区间长度的矩阵(i,j位置的值为大区间第i个位置长度为j的子区间的个数),然后发现这个是如此的,
n=3
111
121
111
n=6
111111
122221
123321
123321
122221
111111
之后我们知道这个值在区间的位置就可以O(1)统计了。
然后发现a[i]是有可能相同的,所以我们最大区间能开多大呢?实际上左边>=就停下了,右边是>再停下来,这样不会漏也不会缺。那么发下码吧。

#include<cstdio>//这个是疯狂优化后的码,速度最快83ms
#include<iostream>
#include<algorithm>
#include<map>
#define N 1000007
using namespace std;
int n,q,lf[N],rf[N],a[N],ma,p[N];
long long f[N];
struct node{
	int w,num;
}x[N<<2];
inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}
inline bool cmp(node a,node b){return a.w<b.w;}
int main(){
	freopen("jxthree.in","r",stdin);
	freopen("jxthree.out","w",stdout);	
	n=read(),q=read();
	for(register int i=1;i<=n;i++)
		x[i].w=read(),x[i].num=i;
	for(register int i=1;i<=q;i++){
		char s[10];
		scanf("%s",s+1);
		x[i+n].w=read();
		x[n+i].num=n+i;
		if(s[1]=='>') p[i]=1;
		if(s[1]=='<') p[i]=-1;
		if(s[1]=='=') p[i]=0;
	}//将所有先读入之后方便离散化
	sort(x+1,x+n+q+1,cmp);//排序
	x[0].w=-1;
	for(register int i=1;i<=n+q;i++){
		if(x[i].w!=x[i-1].w) ma++;
		a[x[i].num]=ma;//离散化
	}
	for(register int i=1;i<=n;i++){
		lf[i]=i;
		int now=i;
		while(a[lf[now]-1]<a[i]&&lf[now]-1!=0) now=lf[now]-1;//注意是小于
		lf[i]=lf[now];
	}//顺着找左边最多到哪
	for(register int i=n;i>=1;i--){
		rf[i]=i;
		int now=i;
		while(a[rf[now]+1]<=a[i]&&rf[now]+1!=n+1) now=rf[now]+1;//这里就是小于等于了
		rf[i]=rf[now];
	}//逆着找右边最多到哪
	for(register int i=1;i<=n;i++){
		long long tot=rf[i]-lf[i]+1,x=i-lf[i]+1;//tot是区间大小,x是当前i在区间的位置
		if(x>(tot+1)/2){x=tot-x+1;}
		if(tot%2==0){f[a[i]]+=(x+1)*x+(tot/2-x)*x*2;}
		else{
			if(x==(tot+1)/2){
				f[a[i]]+=(x-1)*x+x;
			}else f[a[i]]+=(x+1)*x+(tot-x*2)*x;
		}//以上部分是统计答案
	}
	for(register int i=1;i<=ma;i++)f[i]+=f[i-1];//离散化
	for(register int i=1;i<=q;i++){
		int k=a[n+i];
		if(p[i]==1) printf("%lld\n",f[max(ma,k)]-f[k]);
		if(p[i]==-1) printf("%lld\n",f[min(ma,k-1)]);
		if(p[i]==0) printf("%lld\n",f[k]-f[k-1]);
	}//统计答案
}

T3:Description

在这里插入图片描述

Input

在这里插入图片描述

Output

在这里插入图片描述

Sample Input

4

Sample Output

250000005
在这里插入图片描述

Data Constraint

在这里插入图片描述

正解

在这里插入图片描述
不讲了。

T4:树的难题

Description

在这里插入图片描述
这个题正解是淀粉质 点分治,然后我看了一个人的题解并且看懂了,之后自己打了一遍想着第一个AK,结果40分,然后才发现那个人的码跑的超级慢(要求2秒跑3.5秒)于是就这样了。(另外现在也没人AK)

猜你喜欢

转载自blog.csdn.net/jay_zai/article/details/107697711