zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:string(线段树)

    题目描述

    给定一个由小写字母组成的字符串$s$。

    有$m$次操作,每次操作给定$3$个参数$l,r,x$。

    如果$x=1$,将$s[l]~s[r]$升序排序;

    如果$x=0$,将$s[l]~s[r]$降序排序。

    你需要求出最终序列。


    输入格式

    第一行两个整数$n,m$。
    第二行一个字符串$s$。
    接下来m行每行三个整数$l,r,x$。


    输出格式

    一行一个字符串表示答案。


    样例

    样例输入

    5 2
    cabcd
    1 3 1
    3 5 0

    样例输出

    abdcc


    数据范围与提示

    对于$40\%$的数据,$n,mleqslant 1,000$。
    对于$100\%$的数据,$n,mleqslant 100,000$。


    题解

    看到这道题,我就想到了:[BZOJ4552]:[Tjoi2016&Heoi2016]排序(桶排序)

    然而,那道题我使用桶排序卡过的,时限还是$6,000ms$,所以我当场懵逼,线段树是肯定看出来了,但是不知道该怎么操作……

    打题一定要打正解挖~

    $40\%$算法:

    直接用$sort$搞就好了,重载一下运算符,我觉得我说的每一句都是废话……

    桶排一分也不能多拿(万恶的出题人)。

    时间复杂度:

      $Theta(m imes n)$(桶排序)。

      $Theta( m imes nlog n)$(快排)。

    期望得分:$40$分。

    $100\%$算法:

    因为这道题串中只有26个字母,所以就好说多了,用线段树维护区间内$a~z$的个数,每次修改拆成26个修改就行了。

    时间复杂度:$Theta(26 imes m imes log n)$。

    期望得分:$100$分。


    代码时刻

    $40\%$算法:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,l,r;
    bool x;
    int a[100001];
    char ch[100001];
    int t[50];
    int st,ed;
    void change1()
    {
    	register int maxn=0,minn=20020923,lft=l;
    	for(int i=l;i<=r;i++)
    	{
    		t[a[i]]++;
    		maxn=max(maxn,a[i]);
    		minn=min(minn,a[i]);
    	}
    	for(register int i=minn;i<=maxn;i++)
    		while(t[i])
    		{
    			a[lft++]=i;
    			t[i]--;
    		}
    }
    void change2()
    {
    	int maxn=0,minn=20020923,lft=l;
    	for(register int i=l;i<=r;i++)
    	{
    		t[a[i]]++;
    		maxn=max(maxn,a[i]);
    		minn=min(minn,a[i]);
    	}
    	for(register int i=maxn;i>=minn;i--)
    		while(t[i])
    		{
    			a[lft++]=i;
    			t[i]--;
    		}
    }
    
    int main()
    {
    	st=clock();
    	scanf("%d%d%s",&n,&m,ch+1);
    	for(register int i=1;i<=n;i++)
    		a[i]=ch[i]-'a'+1;
    	while(m--)
    	{
    		scanf("%d%d%d",&l,&r,&x);
    		if(x)change1();
    		else change2();
    	}
    	for(register int i=1;i<=n;i++)
    		printf("%c",(char)a[i]+'a'-1);
    	return 0;
    }
    

    $100\%$算法:

    #include<bits/stdc++.h>
    #define L(x) x<<1
    #define R(x) x<<1|1
    using namespace std;
    int n,m;
    char ch[100001];
    int tr[400001];
    int flag[30];
    void pushup(int x){if(tr[L(x)]==tr[R(x)])tr[x]=tr[L(x)];}
    void pushdown1(int x){if(tr[x])tr[L(x)]=tr[R(x)]=tr[x];}
    void pushdown2(int x){if(tr[x])tr[L(x)]=tr[R(x)]=tr[x];tr[x]=0;}
    void build(int x,int l,int r)
    {
    	if(l==r){tr[x]=ch[l]-'a'+1;return;}
    	int mid=(l+r)>>1;
    	build(L(x),l,mid);
    	build(R(x),mid+1,r);
    	pushup(x);
    }
    void ask(int x,int l,int r,int L,int R)
    {
    	if(r<L||R<l)return;
    	if(L<=l&&r<=R&&tr[x]){flag[tr[x]]+=r-l+1;return;}
    	int mid=(l+r)>>1;
    	pushdown1(x);
    	ask(L(x),l,mid,L,R);
    	ask(R(x),mid+1,r,L,R);
    }
    void change(int x,int l,int r,int L,int R,int v)
    {
    	if(r<L||R<l)return;
    	if((L<=l&&r<=R)||tr[x]==v){tr[x]=v;return;}
    	int mid=(l+r)>>1;
    	pushdown2(x);
    	change(L(x),l,mid,L,R,v);
    	change(R(x),mid+1,r,L,R,v);
    	pushup(x);
    }
    void print(int x,int l,int r)
    {
    	if(tr[x]){for(int i=l;i<=r;i++)printf("%c",(char)tr[x]+'a'-1);return;}
    	int mid=(l+r)>>1;
    	print(L(x),l,mid);
    	print(R(x),mid+1,r);
    }
    int main()
    {
    	scanf("%d%d%s",&n,&m,ch+1);
    	build(1,1,n);
    	while(m--)
    	{
    		int l,r,x;
    		scanf("%d%d%d",&l,&r,&x);
    		memset(flag,0,sizeof(flag));
    		ask(1,1,n,l,r);
    		if(x)for(int i=1;i<=26;i++){change(1,1,n,l,l+flag[i]-1,i);l+=flag[i];}
    		else for(int i=26;i>=1;i--){change(1,1,n,l,l+flag[i]-1,i);l+=flag[i];}
    	}
    	print(1,1,n);
    	return 0;
    }
    

    rp++

  • 相关阅读:
    C# FTP操作
    SWUST OJ(963)
    插入排序(折半插入排序)
    SWUST OJ(962)
    SWUST OJ(961)
    SWUST OJ(960)
    SWUST OJ(957)
    SWUST OJ(956)
    SWUST OJ(955)
    SWUST OJ(954)
  • 原文地址:https://www.cnblogs.com/wzc521/p/11285566.html
Copyright © 2011-2022 走看看