zoukankan      html  css  js  c++  java
  • 【日记】1.13

    1.13

    数列分块

    1.数列分块1:区间加减+单点查询

    思路:边角暴力,整块用lazy标记整体加减。

    #include<bits/stdc++.h>
    using namespace std;
    #define LL long long
    #define mid ((l+r)>>1)
    #define db(x) cout<<#x<<":"<<x<<endl;
    const int M=5e4+20,P=1e9+7;
    int bl_size,blnum[M],v[M],lazy[M];
    void bl_init(int n){
    	bl_size=sqrt(n);
    	for(int i=1;i<=n;++i)
    		blnum[i]=(i-1)/bl_size+1;
    }
    void bl_add(int l,int r,int c){
    	//先加左边多余块
    	for(int i=l;i<=min(blnum[l]*bl_size,r);++i)//l到本块最右边和r最小值
    		v[i]+=c;
    	//再加右边多余块
    	if (blnum[l]!=blnum[r])
    		for(int i=(blnum[r]-1)*bl_size+1;i<=r;++i)//r所在块第一个数到r
    			v[i]+=c;
    	//最后处理整块
    	for(int i=blnum[l]+1;i<=blnum[r]-1;++i)
    		lazy[i]+=c;
    }
    struct TTTT{
    	int n,a[M];
    	void init(){
    		scanf("%d",&n);
    		bl_init(n);
    		for(int i=1;i<=n;++i)
    			scanf("%d",&v[i]);
    	}
    	void run(){
    		init();
    		for(int i=1;i<=n;++i){
    			int op,l,r,c;
    			scanf("%d%d%d%d",&op,&l,&r,&c);
    			if (op==0)
    				bl_add(l,r,c);
    			else
    				printf("%d
    ",v[r]+lazy[blnum[r]]);
    		}
    	}
    }TTT;
    int main(){
    	TTT.run();
    	return 0;
    }
    

    2.数列分块2:区间加减+询问小于x的元素个数

    这里询问需要保证每个块是有序的,这样可以直接二分找到小于x的元素个数。因此加减的时候,对于边角需要暴力修改完之后,对原数组排序。显然排序的结果需要新开一个东西存储。整体部分则直接lazy加减即可。

    对于查询,边角仍然暴力统计,整块的话,对于每个块都在内部lowerbound一下查询小于x的元素个数,最后加起来。

    时间复杂度是(O(nlog nsqrt{nlog n}))

    #include<bits/stdc++.h>
    using namespace std;
    #define LL long long
    #define mid ((l+r)>>1)
    #define db(x) cout<<#x<<":"<<x<<endl;
    #define lf(x) ((x)-1)*bl_size+1
    #define rt(x,n) min((x)*bl_size,(n))
    const int M=2e5+20,P=1e9+7;
    int n,bl_size,blnum[M],lazy[M],v[M];
    vector<int> blk[M];
    inline void reset(int x){
    	blk[x].clear();
    	for(int i=lf(x);i<=rt(x,n);++i)
    		blk[x].push_back(v[i]);
    	sort(blk[x].begin(),blk[x].end());
    }
    inline void bl_init(int n){
    	bl_size=sqrt(n/log(n));
    	for(int i=1;i<=n;++i)
    		blnum[i]=(i-1)/bl_size+1,blk[blnum[i]].push_back(v[i]);
    	for(int i=blnum[1];i<=blnum[n];++i)
    		sort(blk[i].begin(),blk[i].end());
    }
    inline void bl_add(int l,int r,int c){
    	for(int i=l;i<=rt(blnum[l],r);++i)
    		v[i]+=c;
    	reset(blnum[l]);
    	if (blnum[l]!=blnum[r]){
    		for(int i=lf(blnum[r]);i<=r;++i)
    			v[i]+=c;
    		reset(blnum[r]);
    	}
    	for(int i=blnum[l]+1;i<=blnum[r]-1;++i)
    		lazy[i]+=c;
    }
    inline int query(int l,int r,int c){
    	int ans=0;
    	for(int i=l;i<=rt(blnum[l],r);++i)
    		if (v[i]+lazy[blnum[l]]<c)
    			++ans;
    	if (blnum[l]!=blnum[r])
    		for(int i=lf(blnum[r]);i<=r;++i)
    			if (v[i]+lazy[blnum[r]]<c)
    				++ans;
    	for(int i=blnum[l]+1;i<=blnum[r]-1;++i)
    		ans+=lower_bound(blk[i].begin(),blk[i].end(),c-lazy[i])-blk[i].begin();
    	return ans;
    }
    struct TTTT{
    	void init(){
    		scanf("%d",&n);
    		for(int i=1;i<=n;++i)
    			scanf("%d",&v[i]);
    		bl_init(n);
    	}
    	void run(){
    		init();
    		for(int i=1;i<=n;++i){
    			int op,l,r,c;
    			scanf("%d%d%d%d",&op,&l,&r,&c);
    			if(op==0)
    				bl_add(l,r,c);
    			else
    				printf("%d
    ",query(l,r,c*c));
    		}
    	}
    }TTT;
    int main(){
    	TTT.run();
    	return 0;
    }
    

    Wannafly Day 2

    A.托米的字符串

    托米有一个字符串,他经常拿出来玩。这天在英语课上,他学习了元音字母a,e,i,o,u以及半元音y。“这些字母是非常重要的!”,托米这样想着,“那么我如果随机取一个子串,里面元音占比期望会有多大呢?”

    于是,请你求出对于托米的字符串,随机取一个子串,元音(a,e,i,o,u,y)字母占子串长度比的期望是多少。

    输入格式:

    读入一个长度不超过106的只包含小写字母的字符串,即托米的字符串。

    输出格式

    输出所求的期望值,要求相对(绝对)误差不超过10−6。

    输入样例:

    legilimens
    

    输出样例:

    0.446746032
    

    思路:统计每个元音字母的贡献。若元音字母的位置是i,及k=min(i+1,len-i),则包含它的,长度为1-k的字符串的个数是1-k个,贡献为(1*1+2*1/2+3*1/3+……),总和就是k。长度为k+1-len-k+1的字符串的个数都是k个,贡献是([1/(k+1)+...+1/(len-k+1)]*k),可以预处理调和级数分数。最后一部分的个数分别是k-1-1个,也可以预处理,时间复杂度(O(n))

    #include<bits/stdc++.h>
    using namespace std;
    #define LL long long
    #define mid ((l+r)>>1)
    #define db(x) cout<<#x<<":"<<x<<endl;
    const int M=1e6+20,P=1e9+7;
    struct TTTT{
    	int n,len;
    	char s[M];
    	double pre[M],pre2[M];
    	void init(){
    		scanf("%s",s);
    		len=strlen(s);
    		for(int i=1;i<=len;++i)
    			pre[i]=pre[i-1]+1.0/i,pre2[i]=pre2[i-1]+1.0*i/(len-i+1);
    	}
    	void run(){
    		init();
    		double sum=0,ans=0;
    		for(int i=1;i<=len;++i)
    			sum+=i;
    		for(int i=0;i<len;++i)
    			if (s[i]=='a'||s[i]=='e'||s[i]=='i'||s[i]=='o'||s[i]=='u'||s[i]=='y'){
    				int mn=min(len-i,i+1),mx=max(len-i,i+1);
    				ans+=mn+(pre[mx]-pre[mn])*mn+pre2[mn-1];
    			}
    		printf("%.16lf
    ",ans/sum);
    	}
    }TTT;
    int main(){
    	TTT.run();
    	return 0;
    }
    

    B.萨博的方程式

    萨博有个方程式:

    x1xor**x2xor**x3xor...xorxn=k

    其中xor指代位运算中的异或符号。

    萨博同时还对每个未知数限制了范围为0≤x**im**i,希望你计算出解的个数,最终答案对109+7取模后输出。

    输入格式:

    本题设有多组数据,请处理到文件尾,保证数据组数不超过100。

    对于每组测试数据:

    第一行读入2个正整数n,k(n≤50,k<231);

    第二行读入n个非负整数m1,m2,m3,...,m**n(m**i<231)。

    输出格式

    对于每组测试数据,输出一个数,为题目所求。

    输入样例:

    7 127
    64 32 16 8 4 2 1
    6 127
    64 32 16 8 4 2
    4 5
    1 2 3 4
    
          
        
    

    输出样例:

    1
    0
    6
    

    思路:数位DP。

    C. 纳新一百的石子游戏

    D.卡拉巴什的字符串

    卡拉巴什是字符串大师,这天他闲着无聊,又造了个字符串问题。
    给定一个长度为N字符串S,定义后缀i为从第i个位置开始的后缀,即sisi+1...sn,定义lcp(i, j)为后
    缀i和后缀j的最长公共前缀。
    卡拉巴什想要知道,每次他给出一组i,j,你能否快速告诉他lcp(i, j)。
    卡拉巴什的好朋友葫芦是字符串宗师,他认为这个题太无聊,于是他想了另一个问题,假设有一个
    集合{lcp(i, j)|1 ≤ i < j ≤ N},他想知道这个集合的MEX值是多少。一个集合的MEX值为最小的没有
    出现在集合中的非负整数。
    这个问题对卡拉巴什来说太容易了, 于是葫芦想知道, 对于字符串的每一个前缀, 对应的集合
    的MEX值是多少。
    Input
    第一行一个整数T(1 ≤ T ≤ 105
    ),表示数据组数。
    对于每组数据,共一行,表示字符串S(1 ≤ |S| ≤ 106
    )。
    输入数据保证所有字符串长度和不超过5 ∗ 106。保证字符串只包含小写字母。
    Output
    对于每组数据,输出|S|个整数,表示每一个前缀对应的MEX值。
    Example
    standard input standard output
    2
    ababa
    baa
    0 1 2 3 4
    0 1 2

    思路:这题真的是思维题目,确实很难想,但代码很好写。

    1. 需要特判0的情况。如果输入字母全都是相同的,那么输出0。
    2. 否则,集合里面的元素是连续的,从0-mex-1。实际上求的就是集合最大值+1。
    3. 考虑从i转移到i+1的情况,lcp增加的那些,肯定后缀整个都是lcp,也就是后缀整个是另外一个的前缀。这种情况下,这个整个后缀一定是endpos>1的(一次出现在最后,另一次出现在lcp另一个串的对应位置)。因此,lcp增加之后最大的那个,长度为maxlen(fa(last))。这是因为属于last节点的一定都没有增加。
    4. 那么每次检验一下maxlen(fa(last))是否比当前mex更大,如果是就mex=那个。而且你可以发现mex每次一定都只+1。具体看cyy的题解吧。
    #include<bits/stdc++.h>
    using namespace std;
    #define LL long long
    #define db(x) cout<<#x<<":"<<x<<endl;
    const int M=1e6+20;
    char s[M],s2[M];
    struct SAM{
        int last,cnt,ch[M<<1][26],fa[M<<1],len[M<<1];
        int mex;
        void ins(int c){
            int p=last,np=++cnt;
    		last=np,len[np]=len[p]+1;
            for(;p&&!ch[p][c];p=fa[p])
    			ch[p][c]=np;
            if(!p)
    			fa[np]=1;
            else{
                int q=ch[p][c];
                if(len[p]+1==len[q])
    				fa[np]=q;
                else{
                    int nq=++cnt;
    				len[nq]=len[p]+1;
                    memcpy(ch[nq],ch[q],sizeof(ch[q]));
                    fa[nq]=fa[q],fa[q]=fa[np]=nq;
                    for(;ch[p][c]==q;p=fa[p])
    					ch[p][c]=nq;
                }
            }
        }
        void build(){
            scanf("%s",s+1);
    		int lenn=strlen(s+1);
    		last=cnt=1;
            printf("0"),ins(s[1]-'a');
            int p=2;
            while(p<=lenn&&s[p]==s[1])
                ins(s[p]-'a'),printf(" 0"),++p;
            if (p>lenn){
                putchar('
    ');
                for(int i=1;i<=cnt;++i)
                    fa[i]=len[i]=0,memset(ch[i],0,sizeof(ch[i]));
                return;
            }
            mex=p-2;
    		for(int i=p;i<=lenn;i++){
    			ins(s[i]-'a');
                if(len[fa[last]]>mex)
                    ++mex;
                printf(" %d",mex+1);
            }
            putchar('
    ');
            for(int i=1;i<=cnt;++i)
                fa[i]=len[i]=0,memset(ch[i],0,sizeof(ch[i]));
        }
    }sam;
    int main(){
        int T;
        scanf("%d",&T);
        for(int i=1;i<=T;++i)
            sam.build();
        return 0;
    }
    

    K.破忒头的匿名信

    思路:一般提供了和不超过某个值的条件的时候,会有一个性质,不同长度最多sqrt(n)个。

    这道题实际就是AC自动机上dp。对所有模式串建AC自动机,之后直接拿文本串上去跑,每跑到一个节点就跳它的所有match(后面会解释)。这是因为,所有match一定是当前文本串前缀的后缀,所以可以从前面转移过来,由于不同长度最多sqrt(n)个,针对一个节点,同一深度只能有一个match,所以跳的match的个数一定不超过sqrt(n)个,因此证明了时间复杂度。

    要注意,如果跳裸fail树,会T,因为fail树上不一定都是终止节点,这个时候就需要用match进行优化,match表示当前节点及往上的所有fail树上的父亲中,最靠下的终止节点。跳match的时候就是trie[trie[cur].fail].match来跳。详情见代码。处理match和处理fail是同时进行的。

    dp[i]表示文本串1-i最小合成代价,跑到第i+1个点的时候,跳所有match。有一个终止节点就dp[i+1]=min(dp[i+1],dp[i+1-depth]+代价)即可。

    反省:

    1. 字符串题目做的太少,最近又没有复习,不会。很难过。
    2. AC自动机板子要改一下,zbh的板子过于抽象,容易忘掉初始化trie[0].fail=-1。
    #include <bits/stdc++.h>
    using namespace std;
    #define LETTER 26
    #define LL long long
    const int M=5e5+90;
    struct Trie{
        int v,fail,depth,match,next[LETTER];
    }pool[M];
    Trie* const trie=pool+1;
    int cnt;
    char str[M],pstr[M];
    int insert(char *s,int pri){
        int cur=0;
        for (int i=0;s[i];++i){
            int &pos=trie[cur].next[s[i]-'a'];
            if (!pos)
                pos=++cnt;
            cur=pos,trie[cur].depth=i+1;
        }
        if (trie[cur].v==0||trie[cur].v>pri)
            trie[cur].v=pri;
        return cur;
    }
    void build(){
        queue <int>q;q.push(0);
        while(!q.empty()){
            int t=q.front();q.pop();
            for(int i=0;i<LETTER;i++){
                int &cur=trie[t].next[i];
                if(cur){
                    q.push(cur),
                    trie[cur].fail=trie[trie[t].fail].next[i],
                    trie[cur].match=trie[cur].v?cur:trie[trie[cur].fail].match;
                }
                else
                    cur=trie[trie[t].fail].next[i];
            }
        }
    }
    LL dp[M];
    void search(char *s){
        int cur=0;
        for(int i=0;s[i];++i){
            cur=trie[cur].next[s[i]-'a'];
            for(int j=trie[cur].match;j;j=trie[trie[j].fail].match)
                dp[i+1]=min(dp[i+1],dp[i+1-trie[j].depth]+trie[j].v);
        }
    }
    int main(){
        int n;
        trie[0].fail=-1;
        scanf("%d",&n);
        for (int i=1;i<=n;++i){
            int c;
            scanf("%s%d",pstr,&c),insert(pstr,c);
        }
        build();
        scanf("%s",str);
        int len=strlen(str);
        for(int i=1;i<=len;++i)
            dp[i]=1e15;
        search(str);
        if (dp[len]==1e15)
            printf("-1
    ");
        else
            printf("%lld
    ",dp[len]);
        return 0;
    }
    
    

    F.采蘑菇的克拉莉丝

    回头再补

  • 相关阅读:
    关于form表单的相同name问题
    MySQL数据库视图
    Blazor
    查看Oracle正在执行的任务
    比较不错的几款开源的WPF Charts报表控件
    Raft算法
    EntityFramework 使用Linq处理内连接(inner join)、外链接(left/right outer join)、多表查询
    systemd、upstart和system V 枯木
    MRTG生成首页报错解决方法 枯木
    dd备份和恢复 枯木
  • 原文地址:https://www.cnblogs.com/diorvh/p/12190114.html
Copyright © 2011-2022 走看看