zoukankan      html  css  js  c++  java
  • 【uoj131】 NOI2015—品酒大会

    http://uoj.ac/problem/131 (题目链接)

    题意

      给出一个字符串,每个后缀有一个权值${a_i}$,这些后缀两两之间存在公共前缀。问能够组成长度从0~n-1的公共前缀的后缀的方案数以及他们权值的最大乘积。

    Solution

      听LCF说这是水题,就来做了。。

      lyp学长说SAM构出来后就两个东西:在自动机上dp,在后缀树上搞事情。

      于是我们很容易想到在后缀树上dp(感觉在自动机上dp没什么卵用),于是我们把串反过来见后缀自动机,然后建出后缀树,在上面树形dp就可以了。

      假设当前dfs到了节点x。我们用mx[x][0,1]记录当前子树中最大的两个后缀的权值,因为权值可能为负数,所以还要用mn[x][0,1]记录当前子树中最小的两个后缀的权值,sum[x]记录当前子树中后缀的个数。这样你发现这些信息就能够更新长度为0~len[x]的答案,其中len[x]表示其在后缀树中的“深度”(其实深度这个表示并不是特别妥当→_→),那么就可以做了。

    细节

      细节多的抠脚,建议想清楚再写→_→不,是深夜写题果然要不得→_→

    代码

    // uoj131
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define inf 1ll<<60
    #define Pi acos(-1.0)
    #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
    using namespace std;
     
    const int maxn=300010;
    int n;
    char s[maxn];
    LL a[maxn],ans[maxn],cnts[maxn];
    
    namespace SAM {
    	int len[maxn<<1],par[maxn<<1],pos[maxn<<1],ch[maxn<<1][26];
    	int last,Dargen,sz,cnt,head[maxn<<1];
    	LL mx[maxn<<1][2],mn[maxn<<1][2],sum[maxn<<1];
    
    	struct edge {int to,next;}e[maxn<<2];
    	
    	void link(int u,int v) {
    		e[++cnt]=(edge){v,head[u]};head[u]=cnt;
    	}
    	void Extend(int c) {
    		int np=++sz,p=last;last=np;
    		len[np]=pos[np]=len[p]+1;
    		for (;p && !ch[p][c];p=par[p]) ch[p][c]=np;
    		if (!p) par[np]=Dargen;
    		else {
    			int q=ch[p][c];
    			if (len[p]+1==len[q]) par[np]=q;
    			else {
    				int nq=++sz;len[nq]=len[p]+1;
    				memcpy(ch[nq],ch[q],sizeof(ch[q]));
    				par[nq]=par[q];
    				par[np]=par[q]=nq;
    				for (;p && ch[p][c]==q;p=par[p]) ch[p][c]=nq;
    			}
    		}
    	}
    	void build() {
    		last=Dargen=sz=1;
    		for (int i=1;i<=n>>1;i++) swap(s[i],s[n-i+1]),swap(a[i],a[n-i+1]);
    		for (int i=1;i<=n;i++) Extend(s[i]-'a');
    	}
    	void dfs(int x) {
    		mx[x][0]=mx[x][1]=-inf;
    		mn[x][0]=mn[x][1]=inf;
    		if (pos[x]) mn[x][0]=mx[x][0]=a[pos[x]],sum[x]=1;
    		for (int i=head[x];i;i=e[i].next) {
    			dfs(e[i].to);
    			cnts[len[x]]+=sum[x]*sum[e[i].to];
    			sum[x]+=sum[e[i].to];
    			if (mx[x][0]<mx[e[i].to][0]) {
    				mx[x][1]=mx[x][0],mx[x][0]=mx[e[i].to][0];
    			}
    			else if (mx[x][1]<mx[e[i].to][0]) mx[x][1]=mx[e[i].to][0];
    			if (mx[x][1]<mx[e[i].to][1]) mx[x][1]=mx[e[i].to][1];
    			if (mn[x][0]>mn[e[i].to][0]) {
    				mn[x][1]=mn[x][0],mn[x][0]=mn[e[i].to][0];
    			}
    			else if (mn[x][1]>mn[e[i].to][0]) mn[x][1]=mn[e[i].to][0];
    			if (mn[x][1]>mn[e[i].to][1]) mn[x][1]=mn[e[i].to][1];
    		}
    		if (mx[x][0]!=-inf && mx[x][1]!=-inf) ans[len[x]]=max(ans[len[x]],mx[x][0]*mx[x][1]);
    		if (mn[x][0]!=inf && mn[x][1]!=inf) ans[len[x]]=max(ans[len[x]],mn[x][0]*mn[x][1]);
    	}
    	void prepare() {
    		for (int i=2;i<=sz;i++) link(par[i],i);
    		for (int i=0;i<=n;i++) ans[i]=-inf;
    		dfs(Dargen);
    	}
    }
    using namespace SAM;
    
    int main() {
    	scanf("%d",&n);
    	scanf("%s",s+1);
    	for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    	build();
    	prepare();
    	for (int i=n-1;i>=0;i--) {
    		ans[i]=max(ans[i],ans[i+1]);
    		cnts[i]+=cnts[i+1];
    	}
    	for (int i=0;i<n;i++) printf("%lld %lld
    ",cnts[i],ans[i]==-inf ? 0 : ans[i]);
    	return 0;
    }
    

    Solution

      原来后缀数组也是可做的。。因为大的height对小的height统计答案并不会影响,所以我们从大到小枚举所有的height,然后并查集维护一下集合大小以及集合中的最大值和最小值。很好理解,看下代码吧。

    细节

      比后缀自动机少多了。。

    代码

    // uoj131
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<complex>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define inf 1ll<<60
    #define Pi acos(-1.0)
    #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
    using namespace std;
    
    const int maxn=300010;
    int sa[maxn],rank[maxn],height[maxn];
    int n,a[maxn],fa[maxn];
    LL sz[maxn],mx[maxn],mn[maxn],sum[maxn],ans[maxn];
    char s[maxn];
    
    struct data {int w,x,y;}t[maxn];
    
    
    namespace Suffix {
    	int wa[maxn],wb[maxn],ww[maxn];
    	bool cmp(int *r,int a,int b,int l) {
    		return r[a]==r[b] && r[a+l]==r[b+l];
    	}
    	void da(char *r,int *sa,int n,int m) {
    		int i,j,p,*x=wa,*y=wb;
    		for (i=0;i<=m;i++) ww[i]=0;
    		for (i=1;i<=n;i++) ww[x[i]=r[i]]++;
    		for (i=1;i<=m;i++) ww[i]+=ww[i-1];
    		for (i=n;i>=1;i--) sa[ww[x[i]]--]=i;
    		for (p=0,j=1;p<n;j*=2,m=p) {
    			for (p=0,i=n-j+1;i<=n;i++) y[++p]=i;
    			for (i=1;i<=n;i++) if (sa[i]>j) y[++p]=sa[i]-j;
    			for (i=0;i<=m;i++) ww[i]=0;
    			for (i=1;i<=n;i++) ww[x[y[i]]]++;
    			for (i=1;i<=m;i++) ww[i]+=ww[i-1];
    			for (i=n;i>=1;i--) sa[ww[x[y[i]]]--]=y[i];
    			for (swap(x,y),x[sa[1]]=p=1,i=2;i<=n;i++)
    				x[sa[i]]=cmp(y,sa[i-1],sa[i],j) ? p : ++p;
    		}
    	}
    	void calheight(char *r,int *sa,int n) {
    		for (int i=1;i<=n;i++) rank[sa[i]]=i;
    		for (int k=0,i=1;i<=n;i++) {
    			if (k) k--;
    			int j=sa[rank[i]-1];
    			while (r[i+k]==r[j+k]) k++;
    			height[rank[i]]=k;
    		}
    	}
    }
    
    bool cmp(data a,data b) {
    	return a.w>b.w;
    }
    int find(int x) {
    	return fa[x]==x ? x : fa[x]=find(fa[x]);
    }
    int main() {
    	scanf("%d",&n);
    	scanf("%s",s+1);
    	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    	Suffix::da(s,sa,n,300);
    	Suffix::calheight(s,sa,n);
    	for (int i=1;i<=n;i++) {
    		fa[i]=i;sz[i]=1;
    		mx[i]=mn[i]=a[i];
    	}
    	for (int i=2;i<=n;i++) t[i-1]=(data){height[i],sa[i-1],sa[i]};
    	sort(t+1,t+n,cmp);
    	for (int i=0;i<n;i++) ans[i]=-inf;
    	for (int i=1;i<n;i++) {
    		int x=find(t[i].x),y=find(t[i].y);
    		if (x!=y) {
    			fa[x]=y;
    			sum[t[i].w]+=sz[x]*sz[y];
    			sz[y]+=sz[x];
    			ans[t[i].w]=max(ans[t[i].w],max(mx[x]*mx[y],mn[x]*mn[y]));
    			mx[y]=max(mx[y],mx[x]);
    			mn[y]=min(mn[y],mn[x]);
    		}
    	}
    	for (int i=n-2;i>=0;i--) {
    		ans[i]=max(ans[i],ans[i+1]);
    		sum[i]+=sum[i+1];
    	}
    	for (int i=0;i<n;i++) printf("%lld %lld
    ",sum[i],sum[i]==0 ? 0 : ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    jquery获取transform里面的值
    H5左滑删除JS插件
    IOS微信浏览器点击事件不起作用问题
    jquery获取不了ajax动态添加的内容的解决办法
    H5跟ios、android交互跟数据对接
    JS图片赖加载例子
    苹果手机浏览器下拉会闪动的解决办法
    jquery.rotate.js可选抽奖次数和中奖内容的转盘抽奖demo
    Html5的localStorage与sessionStorage五种循序渐进的使用方法
    tar 「解/壓」 包
  • 原文地址:https://www.cnblogs.com/MashiroSky/p/6339311.html
Copyright © 2011-2022 走看看