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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值