Codeforces Round #590 (Div. 3)

 

 

D

题意:给长度为n的字符串,接下来给m个操作,操作一:改变一个位置的字母,操作二:询问一个区间内字母的不同个数

思路:叶子节点代表每个位置对应的字母,父节点二进制状压所有字母就可以,这个题都没有区间修改,太水了。

线段树染色问题简化版

 

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;

const int maxn=1e5+10;
char s[maxn];
int a[maxn];

struct note
{
    int left,right,sum,lazy;
    void up(int val)
    {
        sum=1<<(val-1);
        lazy=val;
    }
} tree[maxn*4];
void pushup(int id)
{
    tree[id].sum=tree[id<<1].sum|tree[id<<1|1].sum;
}
void pushdown(int id)
{
    if(tree[id].lazy)
    {
        tree[id<<1].up(tree[id].lazy);
        tree[id<<1|1].up(tree[id].lazy);
        tree[id].lazy=0;
    }
}

void build(int id,int l,int r)
{
    tree[id].left=l;
    tree[id].right=r;
    if(l==r)
        tree[id].sum=1<<(a[l]-1);
    else
    {
        int mid=(l+r)/2;
        build(id<<1,l,mid);
        build(id<<1|1,mid+1,r);
        pushup(id);
    }
}
int query(int id,int l,int r)
{
    if(l<=tree[id].left&&tree[id].right<=r)
        return tree[id].sum;
    pushdown(id);
    int mid=(tree[id].left+tree[id].right)/2;
    int ans=0;
    if(l<=mid) ans|=query(id<<1,l,r);
    if(r>mid) ans|=query(id<<1|1,l,r);
    return ans;
}

void update(int id,int l,int r,int val)
{
    if(l<=tree[id].left&&tree[id].right<=r)
    {
        tree[id].up(val);
        return;
    }
    pushdown(id);
    int mid=(tree[id].left+tree[id].right)/2;
    if(l<=mid) update(id<<1,l,r,val);
    if(r>mid) update(id<<1|1,l,r,val);
    pushup(id);
}

int main()
{
    scanf("%s",s+1);
    int n;
    scanf("%d",&n);
    int len=strlen(s+1);
    for(int i=1; i<=len; i++)
        a[i]=s[i]-'a'+1;
    build(1,1,len);
    for(int i=1; i<=n; i++)
    {
        int op;
        scanf("%d",&op);
        if(op==1)
        {
            int pos;
            char val;
            scanf("%d %c",&pos,&val);
            update(1,pos,pos,val-'a'+1);
        }
        else
        {
            int l,r;
            scanf("%d%d",&l,&r);
            int sum=query(1,l,r);
            int ans=0;
            while(sum>0)
            {
                if(sum&1) ans++;
                sum=sum>>1;
            }
            printf("%d\n",ans);
        }
    }
}
View Code

 

 

F

不会做,看的题解。

题意:给一个字符串,你可以最多反转一段区间的子串,问这个字符串中没有重复字母的连续子串最长长度。

思路:因为只有20个字母所以要想要状态压缩,然后因为没有重复字母且最多反转一段区间,所以这个问题可以转化为求任意两个不包含相同字母的连续子串长度最大,然后我们状压20个字母表示所有的合法方案,预处理子串中的每个区间,求出每种合法方案对应的最大长度。然后正常思路就是处理出来的state两两比较,会超时。可以采用高维前缀和思想优化,对于每个state其实没有必要两两比较,因为我们可以提前预处理出每个state(包含它的子集)对应的最大值,这样的话每个state+state的补集就是最大值了。

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e6+10;
int f[1<<21];
char s[maxn];
int main()
{
    scanf("%s",s+1);
    int len=strlen(s+1);

    for(int i=1; i<=len; i++)
    {
        int state=0;
        for(int j=i; j<=len; j++)
        {
            int t=s[j]-'a';
            if(state&(1<<t))
                break;
            state=state|(1<<t);
            f[state]=max(f[state],j-i+1);
        }
    }
    for(int i=0; i<1<<20; i++) // 高维前缀和,与每个子集作比较,求出每个集合所能产生最大值 
        for(int j=0; j<20; j++)
        {
            if(i&(1<<j))
                f[i]=max(f[i],f[i^(1<<j)]);
        }
    int maxx=0;
    for(int i=0; i<1<<20; i++)
    {
//        int t=((1<<20)-1)^i; 两个都是求补集 
        int t=((1<<20)-1-i);
              maxx=max(maxx,f[i]+f[t]);
    }
    printf("%d",maxx);
}
View Code

 

 

 

 

 

posted @ 2019-10-03 19:15  paranoid。  阅读(231)  评论(0编辑  收藏  举报