YBTOJ 字符串排序

YBTOJ 字符串排序

在这里插入图片描述

这道题卡了快一天QAQ
最开始照着书上的提示写了一个程序,死活T40,然后我找到CF588E原题交了一遍AC了,就以为是评测机的问题,于是加上了各种卡常优化如火车头 ,虽然多过了一个点到了50分但还是差7msAC。
在这里插入图片描述

为了纪念我把50分TLE代码放一下:

#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
#include <immintrin.h>
#pragma GCC optimize(2)
%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
%:pragma GCC optimize("-fgcse")
%:pragma GCC optimize("-fgcse-lm")
%:pragma GCC optimize("-fipa-sra")
%:pragma GCC optimize("-ftree-pre")
%:pragma GCC optimize("-ftree-vrp")
%:pragma GCC optimize("-fpeephole2")
%:pragma GCC optimize("-ffast-math")
%:pragma GCC optimize("-fsched-spec")
%:pragma GCC optimize("unroll-loops")
%:pragma GCC optimize("-falign-jumps")
%:pragma GCC optimize("-falign-loops")
%:pragma GCC optimize("-falign-labels")
%:pragma GCC optimize("-fdevirtualize")
%:pragma GCC optimize("-fcaller-saves")
%:pragma GCC optimize("-fcrossjumping")
%:pragma GCC optimize("-fthread-jumps")
%:pragma GCC optimize("-funroll-loops")
%:pragma GCC optimize("-fwhole-program")
%:pragma GCC optimize("-freorder-blocks")
%:pragma GCC optimize("-fschedule-insns")
%:pragma GCC optimize("inline-functions")
%:pragma GCC optimize("-ftree-tail-merge")
%:pragma GCC optimize("-fschedule-insns2")
%:pragma GCC optimize("-fstrict-aliasing")
%:pragma GCC optimize("-fstrict-overflow")
%:pragma GCC optimize("-falign-functions")
%:pragma GCC optimize("-fcse-skip-blocks")
%:pragma GCC optimize("-fcse-follow-jumps")
%:pragma GCC optimize("-fsched-interblock")
%:pragma GCC optimize("-fpartial-inlining")
%:pragma GCC optimize("no-stack-protector")
%:pragma GCC optimize("-freorder-functions")
%:pragma GCC optimize("-findirect-inlining")
%:pragma GCC optimize("-fhoist-adjacent-loads")
%:pragma GCC optimize("-frerun-cse-after-loop")
%:pragma GCC optimize("inline-small-functions")
%:pragma GCC optimize("-finline-small-functions")
%:pragma GCC optimize("-ftree-switch-conversion")
%:pragma GCC optimize("-foptimize-sibling-calls")
%:pragma GCC optimize("-fexpensive-optimizations")
%:pragma GCC optimize("-funsafe-loop-optimizations")
%:pragma GCC optimize("inline-functions-called-once")
%:pragma GCC optimize("-fdelete-null-pointer-checks")
#include<bits/stdc++.h>
using namespace std;
struct Tree 
{
	int x;
}tree[35][800005];
int n,m,x,y,op,tmp[35],f[800005];
char c[500005];
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
	{
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
	{
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
inline void up_all(int k) {for(register int i=0;i<26;++i) tree[i][k].x=tree[i][k<<1].x+tree[i][k<<1|1].x;}
inline void Add(int k,int l,int r,int pl) 
{
	f[k]=pl;
	for(int i=0;i<26;++i) tree[i][k].x=0;
	tree[pl][k].x=r-l+1;
}
inline void down(int k,int l,int r) 
{
	if(f[k]==-1) return;
	if(l!=r) 
	{
		int mid=(l+r)>>1;
		Add(k<<1,l,mid,f[k]);
		Add(k<<1|1,mid+1,r,f[k]);
	}
	f[k]=-1;
}
inline void build(int k,int l,int r) 
{
	if(l==r)
	{
		tree[c[l]-'a'][k].x=1;
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	up_all(k);
}
inline int get_sum(int k,int l,int r,int L,int R,int pl) 
{
	if(L<=l&&r<=R) 
	{
		return tree[pl][k].x;
	}
	down(k,l,r);
	int mid=(l+r)>>1;
	int re=0;
	if(L<=mid) re+=get_sum(k<<1,l,mid,L,R,pl);
	if(mid+1<=R) re+=get_sum(k<<1|1,mid+1,r,L,R,pl);
	return re;
}
inline void add_num(int k,int l,int r,int L,int R,int pl) 
{
	if(L>R) return;
	if(L<=l&&r<=R) 
	{
		Add(k,l,r,pl);
		return;
	}
	down(k,l,r);
	int mid=(l+r)>>1;
	if(L<=mid) add_num(k<<1,l,mid,L,R,pl);
	if(mid+1<=R) add_num(k<<1|1,mid+1,r,L,R,pl);
	up_all(k);
}
inline void work_up(int x,int y) 
{
	for(register int i=0;i<26;++i) tmp[i]=get_sum(1,1,n,x,y,i);
	int k=x;
	for(register int i=0;i<26;++i) 
	{
		add_num(1,1,n,k,k+tmp[i]-1,i);
		k+=tmp[i];
		if(k>y) return;
	}
}
inline void work_down(int x,int y) 
{
	for(register int i=0;i<26;++i) tmp[i]=get_sum(1,1,n,x,y,i);
	int k=x;
	for(register int i=25;i>=0;--i) 
	{
		add_num(1,1,n,k,k+tmp[i]-1,i);
		k+=tmp[i];
		if(k>y) return;
	}
}
inline void write(int k,int l,int r) 
{
	if(l==r) 
	{
		for(register int i=0;i<26;++i)
			if (tree[i][k].x) 
			{
				printf("%c",'a'+i);
				return;
			}
	}
	down(k,l,r);
	int mid=(l+r)>>1;
	write(k<<1,l,mid);
	write(k<<1|1,mid+1,r);
}
signed main() 
{
	memset(f,-1,sizeof(f));
	n=read(),m=read();
	scanf("%s",c+1);
	build(1,1,n);
	while(m--) 
	{
		x=read(),y=read(),op=read();
		if(op==1) 
		{
			work_up(x,y);
		}
		else 
		{
			work_down(x,y);
		}
    }
	write(1,1,n);
	return 0;
}

至于为什么没用clock()强行卡时间……其实我不会
翻来覆去找到了某大佬要代码观摩,发现复杂度还是差的挺大。呜呜呜,我太菜了
在这里插入图片描述

AC代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,w,l,r,len[27];
char s[100010];
struct c
{
	int l,r,x;
}tree[1000010];
void build(int k,int l,int r)
{
	tree[k].l=l,tree[k].r=r;
	if(l==r)
	{
		tree[k].x=s[l]-'a'+1;
		return;
	}
	int mid=(l+r)/2;
	build(k*2,l,mid);
	build(k*2+1,mid+1,r);
	if(tree[k*2].x==tree[k*2+1].x)
		tree[k].x=tree[k*2].x;
}
void up(int k,int x,int y)
{
	if(tree[k].l>=x&&tree[k].r<=y&&tree[k].x!=0)
	{
		len[tree[k].x]+=tree[k].r-tree[k].l+1;
		return; 
	}
	if(tree[k].x)
	{
		tree[k*2].x=tree[k].x;
		tree[k*2+1].x=tree[k].x;
	}
	int mid=(tree[k].l+tree[k].r)/2;
	if(x<=mid)up(k*2,x,y);
	if(y>mid)up(k*2+1,x,y);
}
void change(int k,int x,int y,int v)
{
	if(tree[k].l>=x&&tree[k].r<=y)
	{
		tree[k].x=v;
		return; 
	}
	if(tree[k].x)
	{
		tree[k*2].x=tree[k].x;
		tree[k*2+1].x=tree[k].x;
		tree[k].x=0;
	}
	int mid=(tree[k].l+tree[k].r)/2;
	if(x<=mid)change(k*2,x,y,v);
	if(y>mid)change(k*2+1,x,y,v);
	if(tree[k*2].x==tree[k*2+1].x)
		tree[k].x=tree[k*2].x;
}
void print(int x)
{
	if(tree[x].x)
	{
		int l=tree[x].r-tree[x].l+1;
		for(int i=1;i<=l;i++)
			putchar(tree[x].x+'a'-1);
		return;
	}
	print(x*2);print(x*2+1);
}
int main()
{
	scanf("%d%d",&n,&m);
	scanf("%s",s+1);
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		
		scanf("%d%d%d",&l,&r,&w);
		memset(len,0,sizeof(len));
		up(1,l,r);
		int ll=l;
		if(w==1)
		{
			for(int i=1;i<=26;i++)
			{
				if(len[i])
				{
					change(1,ll,ll+len[i]-1,i);
					ll+=len[i];
				}
			}
		}
		if(w==0)
		{
			for(int i=26;i>0;i--)
			{
				if(len[i])
				{
					change(1,ll,ll+len[i]-1,i);
					ll+=len[i];
				}
			}
		}
	}
	print(1);
	return 0;
}

这道题的总体思路我就不啰嗦了,你搜到这篇博客说明你肯定有书,书上讲的应该比我好。我着重分析一下大佬的码到底优在哪里,以至于复杂度差的如此之多。

最明显的差别在于求每个字符的区间长度上,AC代码只用了一个函数就完成了,而50分代码则先后调用了三个函数。主要区别在于:50分代码是通过先移动大区间,找到小区间,在小区间中找到某一种字符的长度,然后累加,而AC代码则是通过枚举字符区间的左右端点判断该区间是否在目标区间内,然后直接加。 这样效率就会高很多,而且50分代码顺序倒序要分别求一下每个字符的区间长度,这样效率无疑减半。
通过这道题,我们可以知道线段树的题用的是线段树的思想,而不是死套模板,要灵活变通。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值