【日记】12.12

12.12日记

CF

  1. 1234D:https://codeforces.com/problemset/problem/1234/D

其实直接对每个字母建一个树状数组,建26个即可。每次询问区间和,如果不为0,说明有这个字母,答案+1。时间复杂度\(O(26n\log n)\)

#include
using namespace std;
const int M=1e5+20;
int c[27][M],n;
char s[M];
inline int lowbit(int x){return x&(-x);}
inline void operate(int pos,int k){
    for (int i=pos;i<=n;i+=lowbit(i))
        c[s[pos]-'a'][i]+=k;
}
inline int query(int ch,int x){//1-x的和
    int ans=0;
    for(int j=x;j;j-=lowbit(j))
        ans+=c[ch][j];
    return ans;
}
inline int BITquery(int l,int r){
    int ans=0;
    for(int i=0;i<=25;++i)
        ans+=(query(i,r)!=query(i,l-1));
    return ans;
}
int main(){
    scanf("%s",s+1);
    n=strlen(s+1);
    for(int i=1;i<=n;++i)
        operate(i,1);
    int q;
    scanf("%d",&q);
    for(int i=1;i<=q;++i){
        int op;
        scanf("%d",&op);
        if (op==1){
            int pos;
            char sc[2];
            scanf("%d%s",&pos,sc);
            operate(pos,-1);
            s[pos]=sc[0];
            operate(pos,1);
        }
        else{
            int l,r;
            scanf("%d%d",&l,&r);
            printf("%d\n",BITquery(l,r));
        }
    }
    return 0;
}
  1. 1185C2:https://codeforces.com/problemset/problem/1185/C2

这道题可以转化成:1-i区间内,挑出尽可能多的数,同时保证和

就是直接设置tot数组,存储目前为止,各个数字出现的次数。这样每次都从小往大暴力统计和,找到可以挑出的数的最大个数,再一减就得到答案了。

时间复杂度\(O(100n)\)

还有一个\(O(n\log ^2n)\)的做法,就是首先读入后排序,然后再按原来顺序依次读入数据,建立树状数组,放到对应的排序后的位置上,然后维护区间和(和区间数的个数,相当于两个树状数组),之后二分询问区间和(1-x),找到最靠右的x,满足区间和

#include
using namespace std;
const int M=2e5+20;
int tot[105];
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        int c;
        scanf("%d",&c);
        int T=m-c,p=1;
        while(p<=100&&T>=tot[p]*p)
            T-=tot[p]*p,++p;
        int ans=0;
        for(int j=1;j<=p-1;++j)
            ans+=tot[j];
        ans+=T/p;
        printf("%d",max(i-ans-1,0));
        if(i!=n)
            putchar(' ');
        ++tot[c];
    }
    putchar('\n');
    return 0;
}
  1. 1163B2:https://codeforces.com/problemset/problem/1163/B2

要求1-i区间的数,满足删掉一个之后,所有数出现次数相同。求出最大的i。

思路:开1e5个set,s[i]存放出现次数为i的数都有哪些。另外再开数组存储每个数出现的次数(即对应的集合)。再记录出现次数的最大值mx,那么满足条件只有两种情况:

  • mx=1,所有数都只出现了一次,那么随便删一个就行了,肯定满足。
  • 出现次数为mx的只有一个数,剩下的数都是出现了mx-1次的。这可以用总数来判断。这样的话把mx次的那个删一个就可以了。

那么这题就秒了。复杂度\(O(n\log n)\)

#include
using namespace std;
const int M=1e5+20;
unordered_set s[M];
int cnt[M];
int main(){
    int n,ans=0,mx=0;
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        int c;
        scanf("%d",&c);
        s[cnt[c]].erase(c);
        ++cnt[c];
        s[cnt[c]].insert(c);
        if (mx

明日计划

  1. 树直径,重心,树链剖分
  2. CDQ

你可能感兴趣的:(【日记】12.12)