【日记】12.12

12.12日记

CF

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

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

#include<bits/stdc++.h>
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
",BITquery(l,r));
        }
    }
    return 0;
}
  1. 1185C2:https://codeforces.com/problemset/problem/1185/C2

这道题可以转化成:1-i区间内,挑出尽可能多的数,同时保证和<M-a[i+1]。那么很容易想到,应该从小到大挑数。但每次区间里各个数的数量都在变化,不太容易用普通数据结构维护,因此可以考虑暴力。

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

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

还有一个(O(nlog ^2n))的做法,就是首先读入后排序,然后再按原来顺序依次读入数据,建立树状数组,放到对应的排序后的位置上,然后维护区间和(和区间数的个数,相当于两个树状数组),之后二分询问区间和(1-x),找到最靠右的x,满足区间和<M-a[i+1]。这样也可以。如果考试时间比较大的时候,那么就可以采用这种思想,不过写起来也超级麻烦。

#include<bits/stdc++.h>
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('
');
	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(nlog n))

#include<bits/stdc++.h>
using namespace std;
const int M=1e5+20;
unordered_set<int> 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<cnt[c])
			mx=cnt[c];
		if ((s[mx].size()==1&&s[mx].size()*mx+s[mx-1].size()*(mx-1)==i)||(s[mx].size()*mx==i-1)||(mx==1))
			ans=i;
	}
	printf("%d
",ans);
	return 0;
}

明日计划

  1. 树直径,重心,树链剖分
  2. CDQ
原文地址:https://www.cnblogs.com/diorvh/p/12032658.html