zoukankan      html  css  js  c++  java
  • [loj#2313] [HAOI2017] 供给侧改革

    题意简述

    给定一个随机生成的长度为 (n) 的由0和1构成的字符串 (S)

    (data(l,r)) 表示:在字符串 (S) 中,起始位置在 ([l,r]) 之间的这些后缀之中,具有最长公共前缀的两个后缀的最长公共前缀的长度。

    (m) 次询问,每次给定 (l,r) ,求 (sumlimits_{lleq i < r} data(i,r)) ,不强制在线。

    (n,m leq 10^5)


    想法

    注意到“随机生成”,那么两个长度为 (x) 的字符串完全相同的概率为 (frac{1}{2^x})
    (x=40) 时这个概率就很小了,所以可认为最长公共前缀的长度 (leq 40)
    将询问离线,按 (r) 从小到大排序。
    维护一棵深度40的 (trie) 树。
    从左到右扫,(trie) 树中起始位置在 (i) 的后缀。
    显然 (data(1,i)geq data(2,i) geq ... geq data(i-1,i))
    维护数组 (h[]),其中 (h[x]=y) 表示 (y) 是满足 (data(y,i)geq x) 的最大值。
    换句话说,(data(1,i)geq data(2,i) geq ... geq data(y,i) geq x > data(y+1,i))

    那么对于所有 (r=i) 的询问,将 (h[]) 扫一遍,与 (l) 比比大小再运算便可得到 (ans) ,因为 (h[]) 下标最大40,所以时间上不成问题。

    最后一个问题,每加入一个新后缀时,如何更新 (h[]) 呢?
    (trie) 树中每个点 (x) 都记录一下之前最后访问该点的那个后缀的起始位置 (lst[x]),新加入的后缀走到深度为 (d) 的点 (x) 时,(lst[x]) 开头的后缀与此新后缀的公共前缀至少为 (d) ,也就是 (data(lst[x],i)geq d) ,所以更新 (h[d]=max(h[d],lst[x]))

    总复杂度 (O(40n))


    总结

    抓住“随机”找特点;抓住小的数据点。
    (感觉这个题有点神,虽然代码很短但不太好想……)


    代码

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    int read(){
    	int x=0;
    	char ch=getchar();
    	while(!isdigit(ch)) ch=getchar();
    	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    	return x;
    }
    
    const int N = 100005;
    
    int n,m;
    char s[N];
    struct data{
    	int l,r,id;
    	bool operator < (const data &b) const{ return r<b.r; }
    }d[N];
    int ans[N],h[45];
    
    int cnt,root,ch[N*40][2],lst[N*40];
    void ins(int id){
    	int x=root,y;
    	for(int i=0;i<40 && i+id<=n;i++){
    		y=s[i+id]-'0';
    		if(!ch[x][y]) ch[x][y]=++cnt;
    		x=ch[x][y];
    		h[i+1]=max(h[i+1],lst[x]);
    		lst[x]=id;
    	}
    }
    
    int main()
    {
    	n=read(); m=read();
    	scanf("%s",s+1);
    	for(int i=1;i<=m;i++) d[i].l=read(),d[i].r=read(),d[i].id=i;
    	sort(d+1,d+1+m);
    	
    	root=++cnt;
    	int t=1;
    	for(int i=1;i<=n;i++){
    		if(t>m) break;
    		ins(i);
    		for(int j=39;j>0;j--) h[j]=max(h[j],h[j+1]);
    		while(t<=m && d[t].r==i){
    			for(int j=1;j<=40;j++){
    				if(h[j]<d[t].l) break;
    				ans[d[t].id]+=h[j]-d[t].l+1;
    			}
    			t++;
    		}
    	}
    	for(int i=1;i<=m;i++) printf("%d
    ",ans[i]);
    	
    	return 0;
    } 
    
  • 相关阅读:
    行转列
    multipath 安装配置
    网卡绑定
    numa对MySQL多实例性能影响
    Fatal NI connect error 12170
    REVOKE DBA权限要小心
    Oracle 数据库整理表碎片
    listagg 函数
    10046 事件补充
    tkprof 解释
  • 原文地址:https://www.cnblogs.com/lindalee/p/12363655.html
Copyright © 2011-2022 走看看