12.12日记
CF
其实直接对每个字母建一个树状数组,建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-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-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;
}
明日计划
- 树直径,重心,树链剖分
- CDQ