• 【loj2133】【NOI2015】品酒大会


    Portal --> loj2133

    Solution

      虽然说这题貌似用后缀树之类的好像会更加简短一点。。但是还是因为在智力康复所以就还是用后缀数组好了嗯(哇好感动啊难得那么顺畅写了一道noi的题qwq以及为什么我调试的时候调的都是树状数组的部分啊。。)

      其实仔细想一下,(k)相似的答案对(xin [0,k-1])相似的答案都是由贡献的,也就是说大概是一个后缀和或者后缀最大值的感觉,所以这里就产生了一个最初步的想法我们也许可以用树状数组来统计答案

    ​  然后接着考虑怎么算相似度最大为(k)的贡献

      其实这题的套路和Portal -->「HAOI2016」找相同字符几乎是。。一样的

    ​  同样的我们也是从(height[i])作为区间最小值的贡献这个方向来考虑:我们记(solve(l,r))表示处理(rk)(in [l,r])的后缀对答案的贡献,然后可以用ST表预处理之后(O(1))求得整一个区间内的(height)的最小值(x),也就是这整一个区间内的任意两个后缀的(lcp)一定满足(lcp>=x),即相似值一定(>=x)

    ​  统计方案数的话,我们就直接找到这个最小值的位置(mid),往树状数组中(x)的位置丢入(((mid-1)-l+1)*(r-mid+1))即可(就是在([l,mid-1])中选一个,在([mid,r])中选一个)

      统计美味度的最大值的话,我们也是可以用ST表预处理出(rk)(in [l,r])的后缀开头位置的美味度最大值和最小值,然后查询区间([l,mid-1])中的最大值最小值(记为(recl.fir)(recl.sec)),以及区间([mid,r])中的最大值最小值(记为(recr.fir)(recr.sec)),然后往记录最大值的树状数组中(x)的位置丢入(max(recl.fir*recr.fir,recl.sec*recr.sec))即可

    ​  稍微说明一下需要记录最大和最小值的原因是这里的美味度可能是负数

    ​  然后处理完当前区间之后我们再递归处理([l,mid-1])([mid,r])就好了(同样的这里的区间的开闭之类的也是可以根据个人喜好自己更改的反正。。上面查询什么的跟递归处理的一致就好了)

    ​  然后最后直接对于每一个不同的相似度在两个树状数组中分别(log)的时间查询一下就好了

    ​  最后有一个小细节:在ST表查询(height)最小值的时候,因为我们(height)的定义是与前一个相邻的后缀的(lcp),所以查询的下界需要(+1),而查询美味度最大值的时候并不需要,如果这两个查询放在一起写的话(比如说。。我这种写法qwq),要稍微注意一下

      

      代码大概长这个样子

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define Pr pair<int,int>
    #define mp make_pair
    #define ll long long
    using namespace std;
    const int N=3*(1e5)+10,TOP=20;
    const ll inf=(1e18)+10;
    char s[N];
    ll V[N],lis[N],a[N];
    int n,m;
    bool cmp(int x,int y){return x>y;}
    struct Data{/*{{{*/
    	ll fir,sec;
    	Data(){}
    	Data(ll fir1,ll sec1) {fir=fir1; sec=sec1;}
    	friend Data update(Data x,Data y){
    		static ll tmp[4];
    		tmp[0]=x.fir; tmp[1]=x.sec; tmp[2]=y.fir; tmp[3]=y.sec;
    		sort(tmp,tmp+4,cmp);
    		x.fir=tmp[0]; x.sec=tmp[3];
    		return x;
    	}
    };/*}}}*/
    namespace Bit_sum{/*{{{*/
    	ll c[N];
    	int mx;
    	void init(int _mx){
    		mx=_mx;
    		for (int i=0;i<=mx;++i) c[i]=0;
    	}
    	void insert(int x,ll delta){
    		for (;x;x-=x&-x) c[x]+=delta;
    	}
    	ll query(int x){
    		ll ret=0;
    		for (;x<=mx;x+=x&-x) ret+=c[x];
    		return ret;
    	}
    }/*}}}*/
    namespace Bit_mx{/*{{{*/
    	ll c[N];
    	int mx;
    	void init(int _mx){
    		mx=_mx;
    		for (int i=0;i<=mx;++i) c[i]=-inf;
    	}
    	void insert(int x,ll delta){
    		for (;x;x-=x&-x) c[x]=max(c[x],delta);
    	}
    	ll query(int x){
    		ll ret=-inf;
    		for (;x<=mx;x+=x&-x) ret=max(ret,c[x]);
    		return ret==-inf?0:ret;
    	}
    }/*}}}*/
    namespace Sa{/*{{{*/
    	int a[N],b[N],c[N],sa[N],height[N],rk[N];
    	int mn[N][TOP+1],loc[N][TOP+1];
    	Data val[N][TOP+1];
    	int mx;
    	bool cmp(int x,int y,int len,int *r)
    	{return r[x]==r[y]&&r[x+len]==r[y+len];}
    	void Sort(int n){
    		for (int i=0;i<=mx;++i) c[i]=0;
    		for (int i=1;i<=n;++i) ++c[a[b[i]]];
    		for (int i=1;i<=mx;++i) c[i]+=c[i-1];
    		for (int i=n;i>=1;--i) sa[c[a[b[i]]]--]=b[i];
    	}
    	void get_sa(int n){
    		mx=0;
    		for (int i=1;i<=n;++i) a[i]=s[i]-'a'+1,b[i]=i,mx=max(mx,a[i]);
    		int cnt=0;
    		Sort(n);
    		for (int len=1;cnt<n;len<<=1){
    			cnt=0;
    			for (int i=n-len+1;i<=n;++i) b[++cnt]=i;
    			for (int i=1;i<=n;++i)
    				if (sa[i]>len)
    					b[++cnt]=sa[i]-len;
    			Sort(n);
    			swap(a,b);
    			cnt=1; a[sa[1]]=1;
    			for (int i=2;i<=n;a[sa[i++]]=cnt)
    				if (!cmp(sa[i],sa[i-1],len,b)) ++cnt;
    			mx=cnt;
    		}
    	}
    	void rmq(){
    		for (int i=1;i<=n;++i){
    			mn[i][0]=height[i],loc[i][0]=i;
    			val[i][0]=Data(V[sa[i]],V[sa[i]]);
    		}
    		for (int j=1;j<=TOP;++j)
    			for (int i=n-(1<<j)+1;i>=1;--i){
    				if (mn[i][j-1]<mn[i+(1<<j-1)][j-1]){
    					mn[i][j]=mn[i][j-1],loc[i][j]=loc[i][j-1];
    				}
    				else{
    					mn[i][j]=mn[i+(1<<j-1)][j-1],loc[i][j]=loc[i+(1<<j-1)][j-1];
    				}
    				val[i][j]=update(val[i][j-1],val[i+(1<<j-1)][j-1]);
    			}
    	}
    	void get_height(int n){
    		for (int i=1;i<=n;++i) rk[sa[i]]=i;
    		int k=0;
    		for (int i=1;i<=n;++i){
    			if (k) --k;
    			while (s[i+k]==s[sa[rk[i]-1]+k]) ++k;
    			height[rk[i]]=k;
    		}
    		rmq();
    	}
    	Pr get_lcp(int x,int y,Data &ret){//ranks
    		if (x==y){
    			ret=val[x][0];
    			return mp(n-sa[x]+1,x);
    		}
    		if (x>y) swap(x,y);
    		++x;
    		int len=y-x+1,lg=(int)(log(1.0*len)/log(2.0)),lg1=(int)(log(1.0*(len+1))/log(2.0));
    		ret=update(val[x-1][lg1],val[y-(1<<lg1)+1][lg1]);
    		if (mn[x][lg]<mn[y-(1<<lg)+1][lg])
    			return mp(mn[x][lg],loc[x][lg]);
    		else
    			return mp(mn[y-(1<<lg)+1][lg],loc[y-(1<<lg)+1][lg]);
    	}
    	void solve(int l,int r){
    		if (l>=r) return;
    		Data recl,recr;
    		Pr tmp=get_lcp(l,r,recl);
    		int lcp=tmp.first,mid=tmp.second;
    		get_lcp(l,mid-1,recl);
    		get_lcp(mid,r,recr);
    
    		Bit_mx::insert(lcp+1,max(recl.fir*recr.fir,recl.sec*recr.sec));
    			
    		Bit_sum::insert(lcp+1,1LL*(mid-1-l+1)*(r-mid+1));
    		solve(l,mid-1);
    		solve(mid,r);
    	}
    }/*}}}*/
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("a.in","r",stdin);
    #endif
    	scanf("%d",&n);
    	scanf("%s",s+1);
    	for (int i=1;i<=n;++i) scanf("%lld",V+i);
    	Sa::get_sa(n);
    	Sa::get_height(n);
    	Data ttmp;
    	Bit_mx::init(n); Bit_sum::init(n);
    	Sa::solve(1,n);
    	for (int i=1;i<=n;++i)
    		printf("%lld %lld
    ",Bit_sum::query(i),Bit_mx::query(i));
    }
    
  • 相关阅读:
    css伪类函数 :is() 和 :where()
    卷积层 / 全连接层 的空间和计算复杂度
    mysql中分组获取前三条记录的方法
    sql server数据去重复的四种方法
    C#获取dynamic(动态)实体的属性值
    Sqlserver使用游标循环插入,把上一个select语句的结果当成value值的一部分insert到一张表
    红玫瑰与白玫瑰
    解决vue3中通过params传值以后,刷新页面,params丢失问题。
    mybatis 注解的方式查询
    了解Android已发布的各种版本(即1.0、2.0、3.0、4.0、5.0、6.0、7.0、8.0)
  • 原文地址:https://www.cnblogs.com/yoyoball/p/9370759.html
Copyright © 2020-2023  润新知