zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:platform(后缀数组+二分+线段树)

    题目传送门


    题目描述

    走过奈何桥有一个名叫望乡台的土台,望乡台有个名曰孟婆的老妇人在卖孟婆汤。一生爱恨情仇,一世浮沉得失,都可以随这碗孟婆汤遗忘得干干净净。
    现在有$n$碗孟婆汤摆成一排,汤的品种不超过$26$种,因此我们用小写字母$asim z$来表示一种汤,每碗汤还有一个权值${val}_i$。
    你需要选出若干碗连续摆放的汤喝下去,这些汤必须满足下列条件:
        $alpha.$至少有一碗汤。
        $eta.$这个子串(也就是那些汤)在原串中的所有子串中的字典序降序排名等于这一段汤的权值之和。
    现在你需要知道有多少种选汤的方案。
    注意出现在不同位置、本质相同的子串的排名是相同的且要重复计算方案数,如$"aaa"$这个串,排名为$1$的子串为$"aaa"$,出现了一次;排名为$2$的子串为$"aa"$,出现了两次;排名为$3$的子串为$"a"$,出现了三次。(若还没明白题意的可以看样例$1,3$的解释)。


    输入格式

    第一行一个长度为$n$的由小写字母组成的字符串,每个字符代表一碗汤。
    第二行$n$个非负整数,表示${val}_i$。


    输出格式

    一行一个整数,表示能被选的子串个数$S$。
    接下来$S$行每行两个整数$L,R$,分别表示每个可选子串的左端点与右端点,按照左端点升序为第一关键字,右端点升序为第二关键字排序。


    样例

    样例输入1:

    abcd
    10 0 1 1

    样例输出1:

    3
    1 1
    3 4
    4 4

    样例输入2:

    aaaa
    1 1 1 1

    样例输出2:

    0

    样例输入3:

    aaa
    1 1 1

    样例输出3:

    2
    1 2
    2 3

    样例输入4:

    abdacdbcecbd
    1 3 1 3 3 4 2 2 4 2 1 1

    样例输出4:

    2
    3 8
    9 9


    数据范围与提示

    样例$1$解释:

    我们把所有的子串按字典序从大到小排名:$d,cd,c,bcd,bc,b,abcd,abc,ab,a$。
    那么串$d$的排名为$1$(第一大),权值和为$1$,可以被选。
    串$cd$的排名为$2$,权值和为$2$,可以被选。
    串$a$的排名为$10$,权值和为$10$可以被选。
    其他串则不满足这个条件,故有三个串可以被选。

    样例$3$解释:

    串$a$的排名是$3$,权值和都是$1$。
    串$aa$的排名是$2$,权值和都是$2$,共有两个串$aa$,位置分别为$1 2$和$2 3$。
    串$aaa$的排名是$1$,权值和都是$3$。

    数据范围:

    本题共有$10$测试点:

    对于第$1$个测试点,满足$nleqslant 50$。

    对于第$2,3$个测试点,满足$nleqslant 1,000$。

    对于第$4$个测试点,满足字符串只由一种字符组成,$nleqslant 50,000$。

    对于第$5$个测试点,满足所有汤的权值相同,$nleqslant 50,000$。

    对于第$6,7$个测试点,满足$nleqslant 50,000$。

    对于第$8,9,10$个测试点,满足$nleqslant 200,000$。

    保证$0leqslant {val}_ileqslant 1,000$,且每个测试点满足要求的子串个数不超过$200,000$个。


    题解

    $30\%$算法:

    我们可以暴力统计所有的区间,然后暴力排序,利用前缀和优化$val$的统计,总之就是很暴力。

    时间复杂度:$Theta(n^2)$。

    期望得分:$30$分。

    实际得分:$10sim 30$分。

    $100\%$算法:

    一般这种字符串又没有匹配的问题我们就来考虑后缀数组好啦。

    还是先来偷偷的观察一下数据范围,发现${val}_i>0$,那么对于每一个左端点,如果把它的子串按排名单调下降排,那么权值和将是一个单调不下降的,也就是说,我们可以通过二分找到是否有交点,那么问题就转化问如何快速查找这个排名了。

    在后缀数组中,一个串的本质不同的子串有$sum limits_{i=1}^n n-sa[i]-height[i]+1$个,为方便,设这个数组为$c$,对于每个左端点$l$,设它的一个子串的右端点为$r$,那么这个子串的排名即为$c[n]-c[x[sa[l]-1]-r+l+height[x[sa[l]]]$,然而这个式子仅适用于该子串不是与当前串的前一个排名的串的LCP的子串时成立。可能会有两个串相同怎么办?

    利用线段树为何这个位置所在增加的$LCP$的第一个位置,然后通过这个位置计算出当前位置的排名即可。

    然后我们还能发现,其实$LCP$部分的排名是单调下降的,这样我们可以在处理非$LCP$部分的时候一起二分。

    还有一种做法,是在后缀数组上$RMQ+$二分,但是我不会。

    时间复杂度:$Theta(nlog^2n)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    $30\%$算法:

    #include<bits/stdc++.h>
    using namespace std;
    struct rec
    {
    	int l,r,w;
    }e[1000001];
    int n;
    int a[200001];
    int val[200001];
    char ch[200001];
    int sum,cnt;
    pair<int,int> que[200001];
    void pre_work()
    {
    	for(int i=1;i<=n;i++)
    		for(int j=i;j<=n;j++)
    		{
    			e[++cnt].l=i;
    			e[cnt].r=j;
    			e[cnt].w=val[j]-val[i-1];
    		}
    }
    bool cmp(rec x,rec y)
    {
    	for(int i=0;i<min(x.r-x.l+1,y.r-y.l+1);i++)
    		if(a[x.l+i]!=a[y.l+i])return a[x.l+i]>a[y.l+i];
    	return x.r-x.l>y.r-y.l;
    }
    bool judge(int x)
    {
    	unsigned long long hash1=0,hash2=0;
    	for(int i=e[x].l;i<=e[x].r;i++)
    		hash1=hash1*13131+a[i];
    	for(int i=e[x-1].l;i<=e[x-1].r;i++)
    		hash2=hash2*13131+a[i];
    	if(hash1==hash2)return 0;
    	return 1;
    }
    int main()
    {
    	scanf("%s",ch+1);
    	n=strlen(ch+1);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&val[i]);
    		a[i]=ch[i]-'a'+1;
    		val[i]+=val[i-1];
    	}
    	pre_work();
    	sort(e+1,e+cnt+1,cmp);
    	int hsw=0;
    	for(int i=1;i<=cnt;i++)
    	{
    		if(judge(i))hsw++;
    		if(hsw==e[i].w)
    			que[++sum]=make_pair(e[i].l,e[i].r);
    	}
    	sort(que+1,que+sum+1);
    	cout<<sum<<endl;
    	for(int i=1;i<=sum;i++)
    		printf("%d %d
    ",que[i].first,que[i].second);
    	return 0;
    }
    

    $100\%$算法:

    #include<bits/stdc++.h>
    #define L(x) x<<1
    #define R(x) x<<1|1
    using namespace std;
    int n;
    int a[200001];
    int val[200001];
    char ch[200001];
    int x[200001],y[200001],sa[200001];
    long long c[200001],h[200001];
    int tr[800001];
    int hsw[200001];
    pair<int,int> ans[200001];
    int wzc;
    void change(int x,int l,int r,int L,int R,int w)
    {
    	if(L>r||R<l)return;
    	if(L<=l&&r<=R){tr[x]=w+1;return;};
    	int mid=(l+r)>>1;
    	if(tr[x])tr[L(x)]=tr[R(x)]=tr[x];
    	change(L(x),l,mid,L,R,w);
    	change(R(x),mid+1,r,L,R,w);
    	tr[x]=tr[L(x)]==tr[R(x)]?tr[L(x)]:0;
    }
    int ask(int x,int l,int r,int w)
    {
    	if(tr[x])return tr[x]-1;
    	int mid=(l+r)>>1;
    	if(tr[x])tr[L(x)]=tr[R(x)]=tr[x];(x,l,r);
    	if(w<=mid)return ask(L(x),l,mid,w);
    	else return ask(R(x),mid+1,r,w);
    }
    void get_sa()
    {
    	int m=30;
    	memset(c,0,sizeof(c));
    	for(int i=1;i<=n;i++)c[x[i]=a[i]]++;
    	for(int i=1;i<=m;i++)c[i]+=c[i-1];
    	for(int i=n;i;i--)sa[c[x[i]]--]=i;
    	for(int len=1;len<=n;len<<=1)
    	{
    		int p=0;
    		for(int i=n-len+1;i<=n;i++)y[++p]=i;
    		for(int i=1;i<=n;i++)if(sa[i]>len)y[++p]=sa[i]-len;
    		memset(c,0,sizeof(c));
    		for(int i=1;i<=n;i++)c[x[y[i]]]++;
    		for(int i=1;i<=m;i++)c[i]+=c[i-1];
    		for(int i=n;i;i--)sa[c[x[y[i]]]--]=y[i];
    		for(int i=1;i<=n;i++)x[i]^=y[i]^=x[i]^=y[i];
    		p=1;x[sa[1]]=1;
    		for(int i=2;i<=n;i++)
    			x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+len]==y[sa[i-1]+len]?p:++p;
    		if(p>=n)break;
    		m=p;
    	}
    }
    void get_height()
    {
    	int len=0;
    	for(int i=1;i<=n;h[x[i++]]=len,len=len?len-1:0)
    		if(x[i])while(a[i+len]==a[sa[x[i]-1]+len])len++;
    }
    long long get_ra(int l,int r)
    {
    	if(r-l+1>h[x[l]])return c[n]-c[x[l]-1]-r+l+h[x[l]];
    	int flag=ask(1,1,n,r-l+1);
    	return hsw[flag]+flag-r+l-1;
    }
    int main()
    {
    	scanf("%s",ch+1);
    	n=strlen(ch+1);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&val[i]);
    		a[i]=ch[i]-'a'+1;
    		val[i]+=val[i-1];
    	}
    	get_sa();
    	get_height();
    	for(int i=1;i<=n;i++)
    		c[i]=c[i-1]+n-sa[i]-h[i]+1;
    	for(int i=1;i<=n;i++)
    	{
    		int lft=sa[i],rht=n;
    		while(lft<=rht)
    		{
    			int mid=(lft+rht)>>1;
    			if(val[mid]-val[sa[i]-1]<get_ra(sa[i],mid))lft=mid+1;
    			else rht=mid-1;
    		}
    		if(val[lft]-val[sa[i]-1]==get_ra(sa[i],lft))ans[++wzc]=make_pair(sa[i],lft);
    		if(h[i]<h[i+1])
    		{
    			hsw[h[i]+1]=get_ra(sa[i],sa[i]+h[i]);
    			change(1,1,n,h[i]+1,h[i+1],h[i]+1);
    		}
    	}
    	sort(ans+1,ans+wzc+1);
    	printf("%d
    ",wzc);
    	for(int i=1;i<=wzc;i++)
    		printf("%d %d
    ",ans[i].first,ans[i].second);
    	return 0;
    }
    

    rp++

  • 相关阅读:
    SQL SERVER 全文索引分词
    Json官方介绍
    SQL Try Catch(转载http://www.cnblogs.com/jimmyray/archive/2011/08/02/2125069.html)
    SQL函数记录
    SQL事务处理代码(SQL Server 2000 & 2005)
    SQL通用分页存储过程
    [导入]SoapExtension 1.0 的问题与解决
    BugTracker.NET 汉化手札
    [导入]我对J2EE和.NET的一点理解
    PostgreSQL 8.0.2 应用报告
  • 原文地址:https://www.cnblogs.com/wzc521/p/11417561.html
Copyright © 2011-2022 走看看