“一切都会好起来的。”|

zplqwq

园龄:3年10个月粉丝:25关注:14

来北京随便做做题

CF680D *2400

呃这题真的搞了好久啊,理解能力变差了。

我们假设我们最大可以走到的下标是 xi,最小可以走到的下标是 xj。那么答案就是 xixj+1

而我们发现这个下标一定是累计得到的,就比如第 3 时刻的下标一定是 a1+a2+a3,也就是前缀和。所以在不考虑往 0 的地方填数的情况下,答案就是 max{max{s}min{s}+1}

回到我们一开的假设,根据那个假设,我们可以把这只狗行走的部分分成三段。我们不妨设这只狗先走到最小的下标再走到最大的下标,反之亦然。

  • 第一部分,[1,xi1] 。这一段是这只狗从起点走到最小的下标的过程。

  • 第二部分,[1,xj] 。这一段是这只狗从起点最大的下标的过程。

  • 第三部分,[1,n] 。这一段是这只狗经过了前面一系列移动最后又回到零点的过程。

那么我们发现,真正影响答案的就是 [xi1,xj]。这一部分向扩展的越多,那么我们答案也就越大。

考虑到 n3000 ,那我们不妨 O(n2) 枚举 xixj

我们假设当前这段区间 [xi,xj] 已知能扩展的和是 sum0 的个数是 zero 那么这段区间最多能扩展到的地方是 sum+zero×k

但我们必须要明白一点,就是我们扩展到了最大值有些时候是无法回到零点的。所以我们同时也要算出这段区间外的两个区间总共走了多少,给我们留下了多少的走动空间,

我们不妨调整一下顺序,把 [xi1,xj] 单独考虑。剩下两断合并到一起走,这样显然不影响结果。

那我们同样假设这两段已经扩展的和是 sum0 的个数是 zero

那么这一段留给 [xi1,xj] 空余能走的地方是 sumzero×k 。至于为什么这里是减号,还记得我们一开始做的假设吗?我们设这只狗先走到最小的下标再走到最大的下标,所以 [xi1,xj] 这一段一定是全部向右走,所以剩下两断区间合并起来就是全部向左走,因为最后要回到 0

最后说一下无解的判断,如果这个数列 |sn| 的和大于零的个数乘以 k 的话,那么必然无解,因为这样就算零的部分全填 k (或 k),都无法达到
|sn|

看看关键代码。

//sum 就是前缀和数组,d 是零的个数
int tmp=sum[r]-sum[l-1];int cnt=d[r]-d[l-1];//这个是算[x_{i-1},x_j] 这个区间的和以及0的个数
int tmpp=sum[n]-tmp;int cntt=d[n]-cnt;//剩下两个区间
ans=max(ans,min(abs(tmp+cnt*k),abs(tmpp-cntt*k)));//先走到最小值再走到最大值
ans=max(ans,min(abs(tmp-cnt*k),abs(tmpp+cntt*k)));//先走到最大值再走到最小值

CF1746F Kazaee *2800

我靠,难死我了。最后被 rand 卡了几个小时。

具体思路很有趣,xor hash,不过严格意义上这个是 sum hash。

这题题解太多了就不交了。

就是我们发现如果一段区间内每个数出现的次数都是 k 的正整数倍,那么这一段区间的和一定是 k 的倍数。

所以我们可以每次给 ai 随机加上一个权值 wi 然后每一次判断一下这一段的和是不是 k 是不是 k 的倍数。

具体地,我们使用卡时的方法,重复随机权值并计算。对于每次询问,只要有一次计算出不满足条件就输出 NO。

就好了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=7e5+10;
const int mod=1004535809;
int a[N],c[N],tmp[N],l[N],r[N],k[N],rd[N],lsh[N];
int n,sz,q;
bool ans[N];
mt19937 rnd(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
int randint(int L, int R) {
uniform_int_distribution<int> dist(L, R);
return dist(rnd);
}
int lowbit(int x){return x&-x;}
void add(int x,int k){
while(x<=n){
c[x]+=k;
x+=lowbit(x);
}
}
int gsum(int x){
int sum=0;
while(x){
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
int query(int l,int r){
return gsum(r)-gsum(l-1);
}
signed main(){
srand(time(0));
cin>>n>>q;for(int i=1;i<=n;i++){cin>>a[i];lsh[++sz]=a[i];}
for(int i=1;i<=q;i++){
int opt;cin>>opt;
if(opt==1) {cin>>l[i]>>r[i];lsh[++sz]=r[i];}
else{cin>>l[i]>>r[i]>>k[i];}
}
sort(lsh+1,lsh+1+sz);
sz=unique(lsh+1,lsh+1+sz)-lsh-1;
for(int i=1;i<=n;i++){a[i]=lower_bound(lsh+1,lsh+1+sz,a[i])-lsh;}
for(int i=1;i<=q;i++){
if(k[i]==0){
r[i]=lower_bound(lsh+1,lsh+1+sz,r[i])-lsh;
}
}
while(clock()<CLOCKS_PER_SEC*2.8){
for(int i=1;i<=sz;i++) rd[i]=randint(0, mod-1);
for(int i=1;i<=n;i++) c[i]=0;
for(int i=1;i<=n;i++) tmp[i]=rd[a[i]];
for(int i=1;i<=n;i++) add(i,tmp[i]);
for(int i=1;i<=q;i++){
if(k[i]==0) {
add(l[i],rd[r[i]]-tmp[l[i]]);
tmp[l[i]]=rd[r[i]];
//呃原理大概是 l[i]=tmp[l[i]] 所以 rd[r[i]]-tmp[l[i]]+tmp[l[i]]=rd[r[i]] 所以实现了改变?
}
else{
if((r[i]-l[i]+1)%k[i]) {ans[i]=true;continue;}
if(query(l[i],r[i])%k[i]) ans[i]=true;
}
}
}
for(int i=1;i<=q;i++) {if(k[i]){if(ans[i]==1) puts("NO");else puts("YES");}}
return 0;
}

CF1646 Playing Around the Table *2900

讲真这题挺简单的,就是想到中间状态很难。

牛逼题。

1:1 2 3 4

2:2 3 4 1

3:3 4 1 2

4:4 1 2 3

我们发现,如果要是当前情况变成了这样,那么是一定可以用 n×(n1)2 步调整完毕。

具体地,循环移位,好像解释起来很困难。放一个中间结果理解一下 n=5。

点击查看代码
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 1 3 4 5
1 2 2 4 5
1 2 3 3 5
1 2 3 4 4
2 3 4 5 5
__________________
1 1 2 4 5
1 2 2 3 5
1 2 3 3 4
2 3 4 4 5
1 3 4 5 5
1 1 1 4 5
1 2 2 2 5
1 2 3 3 3
2 3 4 4 4
3 4 5 5 5
__________________
1 1 1 3 5
1 2 2 2 4
2 3 3 3 5
1 3 4 4 4
2 4 5 5 5
1 1 1 2 5
1 2 2 2 3
2 3 3 3 4
3 4 4 4 5
1 4 5 5 5
1 1 1 1 5
1 2 2 2 2
2 3 3 3 3
3 4 4 4 4
4 5 5 5 5
__________________
1 1 1 1 4
2 2 2 2 5
1 3 3 3 3
2 4 4 4 4
3 5 5 5 5
1 1 1 1 3
2 2 2 2 4
3 3 3 3 5
1 4 4 4 4
2 5 5 5 5
1 1 1 1 2
2 2 2 2 3
3 3 3 3 4
4 4 4 4 5
1 5 5 5 5
1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
4 4 4 4 4
5 5 5 5 5
__________________

这个状态我们称之为,中间态,那么如何调整成中间态呢。

注意,因为每个人手中的牌顺序不分先后,所以变成中间态后我们可以全部都当成由小到大排序处理,方便理解。

因此,我们只需要将每一个人手里的牌变成 1n 即可。方法很弱智。

每次找到第一个有重复元素的人,然后把重复的向下传递,下一个人如果有重复的就传递重复的,没有就把上一个人传递过来的传递到下一个人,以此类推。

这样必然可以在有限步内完成传递。可以证明这个步数是 n×(n1)2,但具体证明我也不太会所以先咕着。

然后这题代码感觉很有难度,稍微写点注释。

int check(){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(cnt[i][j]>1) return i; //如果有重复的就返回当前人,这就找到了第一个有重复元素的人
}
}
return 0;
}
void pre(){//变为目标状态
while(1){
int nwep=check();int las=0;//las 是当前传递的元素
if(nwep==0) break;//如果没有找到有重复的就已经是中间态了,break出来
sz++;//这是一次操作
for(int j=1;j<=n;j++){
if(cnt[nwep][j]>1){//找到重复元素
cnt[nwep][j]--;//因为要传递下去,所以个数减一
ans[sz][nwep]=las=j;//ans[i][j]表示第i步第j个人传递的数
//这里只给cnt[nwep][j]--的原因是你只知道要传递给谁但不知道谁传递给他,因为要绕一周才能传递回来
break;
}
}
for(int i=nwep%n+1;i!=nwep;i=i%n+1){//这样就很好的完成了一周的传递,技巧+1,这个枚举的是人
bool flag=0;
for(int j=1;j<=n;j++){
if(cnt[i][j]>1){//如果当前这个元素重复
cnt[i][j]--;cnt[i][las]++;//这里可以直接加是因为不用绕一圈,直接传过来了
ans[sz][i]=las=j;//更新答案和接下来要继续传递的值
flag=1;
break;
}
}
if(flag==0) ans[sz][i]=las;//如果一次都没更新过证明当前人已经是目标状态了,把前面那个人船体过来的东西原封不动传递下去
}
cnt[nwep][las]++;//此时已经转了一圈回来了,所以更新第一个人的
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int x;cin>>x;cnt[i][x]++;//提前记录出现次数
}
}
pre();
for(int i=2;i<=n;i++){//这里要开始传递了,这里是枚举第一个移动哪个,参考上面例子,语言解释不清楚了(
for(int j=1;j<i;j++){
sz++;//一步
for(int k=1;k<=n;k++){
int val=(k+i-j+n)%n;//这里就是阴间循环移位,k+i-j+n 对应了当前移动哪个
if(val==0) val=n;
ans[sz][k]=val;//改答案
//按理来讲这里还要改一下那个桶数组,不过因为不会用到就去掉了
}
}
}
cout<<sz<<"\n";
for(int i=1;i<=sz;i++){
for(int j=1;j<=n;j++){
// if(ans[i][j]==0) cout<<"worng: "<<i<<endl;
cout<<ans[i][j]<<" ";//输出
}
cout<<"\n";
}
return 0;
}

CF1646E Power Board *2200

呃蛮简单题。

第一行单独处理。

我们考虑如果第 i 行的 i 可以被表示为 dk 那么他就有可能重复。考虑如果 d 不一样的话一定不会有重复,所以我们单独考虑 k

显然 k 的上限是 logdn

我们发现对于一个 i ,他给答案的贡献是 1×1,1×2....1×m,2×1,2×2.....2×m.....k×1....k×m 中不同数的个数。

考虑到 k 的最大值是 20,所以我们可以直接枚举 km 然后开个桶来预处理出每个 k 的贡献。

计算的时候我们对于每一个 i 求出他的 k,然后把所有 n 并可以表示为 ix 的数标记掉,因为我们刚刚在计算 i 的时候已经把他贡献算过了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+1e4;
bool vis[N*20];
int viss[N],cnt,ans[N],sum;
int n,m;
signed main(){
cin>>n>>m;
for(int i=1;i<=20;i++){
for(int j=1;j<=m;j++){
if(vis[i*j]==0){
vis[i*j]=1;
cnt++;
}
}
ans[i]=cnt;
// cout<<ans[i]<<" ";
}
// cout<<endl;
for(int i=2;i<=n;i++){
if(viss[i]==1) continue;
int tmp=i,po=0;
while(tmp<=n){
// cout<<tmp<<"qwq\n";
po++;
// cout<<po<<endl;
// cout<<tmp<<" "<<i<<" "<<tmp*i<<endl;
tmp*=i;
if(tmp>n) break;
// cout<<tmp<<endl;
viss[tmp]=1;
}
// cout<<tmp<<" "<<po<<endl;
sum+=ans[po];
}
cout<<sum+1;
return 0;
}

CF1545D AquaMoon and Wrong Coordinate *3000

这题他妈的就不是人能想出来的。

注意以下题解默认初始位置是 1 时刻。

首先我们确定哪一行出了问题是平凡的。

t 时刻时,这些点的坐标和应该是 i=1nxi+t×vi

t+1 时刻时,这些点的坐标和应该是 i=1nxi+(t+1)×vi

然后把这两个式子相减发现是 i=1nvi,至于这个 vi 是多少并不重要,因为 vi 不会变,所以我们把每个时刻的坐标和相减,我们找到 第一个 x 使得第 x 行减去第 x1 行的值与其他的值不同,那么问题出在第 x 行上。

确定哪一行,如何确定具体该改多少呢。这里就要用到人类智慧了。

我们考虑记函数 f(t)=i=1m(xi+t×vi)2 即每一行位移的平方和。

我们展开一下 f(t+1)f(t1),发现一个式子。

f(t+1)+f(t1)2f(t)=i=1m2vi

所以可以得到,任意连续三个时刻的坐标的平方和同样是固定的。

而我们又知道哪一时刻出了问题,所以我们枚举这一时刻的每一个坐标把他改成 k ,如果哪一次改了之后上式的值与正常的值相等,那么这个位置就要改成 k ,这题就做完了。

还有几个细节,第一个是应该改成什么。

我们假设两个时刻时间应该差 x 但实际上差了 y,那么因为我们假设了其他位置没错,所以一定是这个位置的问题,所以说当前坐标应该 y+x

然后还有因为我们连续最后一步要连续取三个时刻当作标准值,所以如果问题出在前三个,那么标准值就取 t=5,6,7 时刻的 f(t),否则就取前三个。

具体看代码。

for(int i=2;i<=k;i++){
if(mp[s[i]-s[i-1]]==1){
flag=i;
if(i<=3) flag2=5;
else flag2=2;
break;
}
}
cout<<flag-1<<" ";//这里要减一是因为题目默认初始时刻是0,而我是按照1处理的
int tmp=s[flag2+1]-s[flag2];int tmp2=v[flag2+1]+v[flag2-1]-2*v[flag2];
for(int i=1;i<=n;i++){
int ans=pos[flag][i]-(s[flag]-s[flag-1])+tmp;
// cout<<ans<<" "<<tmp<<" "<<" "<<s[flag]-s[flag-1]<<endl;
if(v[flag+1]+v[flag-1]-2*(v[flag]+ans*ans-pos[flag][i]*pos[flag][i])==tmp2){
cout<<ans<<"\n";
cout.flush();
return 0;
}
}

CF896C *2600

ODT 模板题。

大概思路就是用一个set来维护,然后就暴力。感觉很神奇,但复杂度为什么是对的我也不知道。

// LUOGU_RID: 103272881
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
const int N=1e5+10;
int ksm(int a,int b,int mod){
int ret=1;a%=mod;
while(b){
if(b&1) ret=ret*a%mod;
a=a*a%mod;a%=mod;b>>=1;
}
return ret%mod;
}
int a[N],n,m,seed,vmax;
int rnd(){
int ret=seed;
seed=(seed*7+13)%mod;
return ret;
}
struct node{
int l,r;
mutable int v;
node(int l,int r=0,int v=0):l(l),r(r),v(v){}
bool operator <(const node &a) const{
return l<a.l;
}
};
set<node> s;
set<node>::iterator split(int pos){
set<node>::iterator it=s.lower_bound(node(pos));
// cout<<it->v<<endl;
if(it!=s.end() and it->l==pos){
return it;
}
it--;if(it->r<pos) return s.end();
int l=it->l;int r=it->r;int v=it->v;
s.erase(it);s.insert(node(l,pos-1,v));
return s.insert(node(pos,r,v)).first;
}
void add(int l,int r,int x){
// cout<<"qwq\n";
set<node>::iterator itr=split(r+1),itl=split(l);
for(set<node>::iterator i=itl;i!=itr;i++){
i->v+=x;
}
// cout<<"qwq\n";
}
void change(int l,int r,int x){
set<node>::iterator itr=split(r+1),itl=split(l);
s.erase(itl,itr);
s.insert(node(l,r,x));
}
struct nodee{
nodee(){}
int num,cnt;
bool operator <(const nodee &a) const{
return num<a.num;
}
nodee(int num,int cnt): num(num),cnt(cnt){}
}rk[N];
int rnk(int l,int r,int x){
set<node> ::iterator itr=split(r+1),itl=split(l);
int sz=0;
for(set<node>::iterator i=itl;i!=itr;i++){
rk[++sz]=nodee(i->v,i->r-i->l+1);
}
sort(rk+1,rk+1+sz);
int k;
for(k=1;k<=sz;k++){
if(x>rk[k].cnt) x-=rk[k].cnt;
else break;
}
return rk[k].num;
}
int calc(int l,int r,int x,int y){
set<node> ::iterator itr=split(r+1),itl=split(l);
int ans=0;
for(set<node>::iterator i=itl;i!=itr;i++){
ans+=(ksm(i->v,x,y)*(i->r-i->l+1)%y)%y;
ans%=y;
}
return ans;
}
signed main(){
cin>>n>>m>>seed>>vmax;
for(int i=1;i<=n;i++){
a[i]=(rnd()%vmax)+1;
s.insert(node(i,i,a[i]));
}
for(int i=1;i<=m;i++){
int op=(rnd()%4)+1;
int l=(rnd()%n)+1;
int r=(rnd()%n)+1;int x=0,y=0;
if(l>r) swap(l,r);
if(op==3) x=(rnd()%(r-l+1))+1;
else x=(rnd()%vmax)+1;
if(op==4) y=(rnd()%vmax)+1;
if(op==1){add(l,r,x);}
if(op==2){change(l,r,x);}
if(op==3){cout<<rnk(l,r,x)<<"\n";}
if(op==4){cout<<calc(l,r,x,y)<<"\n";}
}
return 0;
}

把几个用珂朵莉树草过去的给挂着,思路不写,太简单了(

P4344 SHOI2015 脑洞治疗仪 紫

CF817F MEX Queries *2300

P2572 [SCOI2010] 序列操作 紫

思路不难想,类似小白逛公园。维护八个值,0/1 的个数,从左/右边开始的最长连续0/1的个数,整个区间的 0/1 个数,然后直接模仿小白逛公园就可以了。但为啥查询还有push_up啊,迷惑。写了 4.5k,困难的。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct node{
int w,b,lw,lb,rw,rb,mw,mb,l,r,len,add,add2;//add 区间反转 add2 区间赋值
// node(int w=0,int b=0,int lw=0,int lb=0,int rw=0,int rb=0,int mw=0,int mb=0,int l=0,int r=0,int len=0,int add=0,int add2=0):
// w(w),b(b),lw(lw),lb(lb),rw(rw),rb(rb),mw(mw),mb(mb),l(l),r(r),len(len),add(add),add2(add2){}
}t[N*4];
int n,m,a[N];
void out_put(int p){
cout<<p<<" "<<t[p].add<<" "<<t[p].w<<" "<<t[p].b<<" "<<t[p].lw<<" "<<t[p].lb<<" "<<t[p].rw<<" "<<t[p].rb<<" "<<t[p].mw<<" "<<t[p].mb<<endl;
}
void push_up(int p){
// if(p==4){
// out_put(8);out_put(9);
// }
t[p].w=t[p*2].w+t[p*2+1].w;t[p].b=t[p*2].b+t[p*2+1].b;
if(t[p*2].b==0) t[p].lw=(t[p*2].w+t[p*2+1].lw);
else t[p].lw=t[p*2].lw;
if(t[p*2].w==0) t[p].lb=(t[p*2].b+t[p*2+1].lb);
else t[p].lb=t[p*2].lb;
if(t[p*2+1].b==0) t[p].rw=(t[p*2].rw+t[p*2+1].w);
else t[p].rw=t[p*2+1].rw;
if(t[p*2+1].w==0) t[p].rb=(t[p*2].rb+t[p*2+1].b);
else t[p].rb=t[p*2+1].rb;
t[p].mw=max(max(t[p*2].mw,t[p*2+1].mw),t[p*2].rw+t[p*2+1].lw);
t[p].mb=max(max(t[p*2].mb,t[p*2+1].mb),t[p*2].rb+t[p*2+1].lb);
// if(p==2){cout<<"qwq ";out_put(p);out_put(p*2);out_put(p*4+1);}
}
void build(int l,int r,int p){
t[p].l=l;t[p].r=r;t[p].len=r-l+1;t[p].add2=-1;
if(l==r){
if(a[l]==0){
t[p].w=0;t[p].b=1;t[p].lw=0;t[p].lb=1;t[p].rw=0;t[p].rb=1;t[p].mw=0;t[p].mb=1;
}
else{
t[p].w=1;t[p].b=0;t[p].lw=1;t[p].lb=0;t[p].rw=1;t[p].rb=0;t[p].mw=1;t[p].mb=0;
}
return ;
}
int mid=(l+r)/2;
build(l,mid,p*2);build(mid+1,r,p*2+1);push_up(p);
}
void push_down(int p){
if(t[p].add2==0){//区间赋0
t[p*2].add2=0;t[p*2+1].add2=0;t[p*2].add=0;t[p*2+1].add=0;
t[p*2].w=0;t[p*2].lw=0;t[p*2].rw=0;t[p*2].mw=0;
t[p*2+1].w=0;t[p*2+1].lw=0;t[p*2+1].rw=0;t[p*2+1].mw=0;
t[p*2].b=t[p*2].len;t[p*2].lb=t[p*2].len;t[p*2].rb=t[p*2].len;t[p*2].mb=t[p*2].len;
t[p*2+1].b=t[p*2+1].len;t[p*2+1].lb=t[p*2+1].len;t[p*2+1].rb=t[p*2+1].len;t[p*2+1].mb=t[p*2+1].len;
t[p].add2=-1;
}
if(t[p].add2==1){//区间赋1
t[p*2].add2=1;t[p*2+1].add2=1;t[p*2].add=0;t[p*2+1].add=0;
t[p*2].w=t[p*2].len;t[p*2].lw=t[p*2].len;t[p*2].rw=t[p*2].len;t[p*2].mw=t[p*2].len;
t[p*2+1].w=t[p*2+1].len;t[p*2+1].lw=t[p*2+1].len;t[p*2+1].rw=t[p*2+1].len;t[p*2+1].mw=t[p*2+1].len;
t[p*2].b=0;t[p*2].lb=0;t[p*2].rb=0;t[p*2].mb=0;
t[p*2+1].b=0;t[p*2+1].lb=0;t[p*2+1].rb=0;t[p*2+1].mb=0;
t[p].add2=-1;
}
if(t[p].add!=0){//区间反转
t[p*2].add^=1;t[p*2+1].add^=1;
swap(t[p*2].w,t[p*2].b);swap(t[p*2].lw,t[p*2].lb);swap(t[p*2].rw,t[p*2].rb);swap(t[p*2].mw,t[p*2].mb);
swap(t[p*2+1].w,t[p*2+1].b);swap(t[p*2+1].lw,t[p*2+1].lb);swap(t[p*2+1].rw,t[p*2+1].rb);swap(t[p*2+1].mw,t[p*2+1].mb);
t[p].add=0;
}
}
void change(int l,int r,int p,int k){
if(l<=t[p].l and t[p].r<=r){
if(k==0){
t[p].add=0;
t[p].w=0;t[p].lw=0;t[p].rw=0;t[p].mw=0;
t[p].b=t[p].len;t[p].lb=t[p].len;t[p].rb=t[p].len;t[p].mb=t[p].len;
t[p].add2=0;
}
if(k==1){
t[p].add=0;
t[p].b=0;t[p].lb=0;t[p].rb=0;t[p].mb=0;
t[p].w=t[p].len;t[p].lw=t[p].len;t[p].rw=t[p].len;t[p].mw=t[p].len;
t[p].add2=1;
}
if(k==2){
swap(t[p].w,t[p].b);swap(t[p].lw,t[p].lb);swap(t[p].rw,t[p].rb);swap(t[p].mw,t[p].mb);
t[p].add^=1;
}
// cout<<k<<" "<<t[p].l<<" "<<t[p].r<<" "<<t[p].add<<endl;
// push_down(p);
// out_put(p);
// out_put(2);
return ;
}
int mid=(t[p].l+t[p].r)/2;
push_down(p);push_up(p);
if(l<=mid) change(l,r,p*2,k);
if(r>mid) change(l,r,p*2+1,k);
push_down(p);push_up(p);
}
node query(int l,int r,int p){
if(l<=t[p].l and t[p].r<=r) return t[p];
int mid=(t[p].l+t[p].r)/2;
push_down(p);push_up(p);
node ans;
if(r<=mid) ans=query(l,r,p*2);
else if(l>mid) ans=query(l,r,p*2+1);
else{
node tmp=query(l,r,p*2);
node tmp2=query(l,r,p*2+1);
ans.w=tmp.w+tmp2.w;
ans.b=tmp.b+tmp2.b;
if(tmp.b==0) ans.lw=(tmp.w+tmp2.lw);
else ans.lw=tmp.lw;
if(tmp.w==0) ans.lb=(tmp.b+tmp2.lb);
else ans.lb=tmp.lb;
if(tmp2.b==0) ans.rw=(tmp.rw+tmp2.w);
else ans.rw=tmp2.rw;
if(tmp2.w==0) ans.rb=(tmp.rb+tmp2.b);
else ans.rb=tmp2.rb;
ans.mw=max(max(tmp.mw,tmp2.mw),tmp.rw+tmp2.lw);
ans.mb=max(max(tmp.mb,tmp2.mb),tmp.rb+tmp2.lb);
}
push_up(p);
return ans;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,n,1);
while(m--){
int opt,l,r;cin>>opt>>l>>r;
l++;r++;
if(opt==0){
change(l,r,1,0);
}
if(opt==1){
change(l,r,1,1);
// cout<<t[2].w<<endl;
}
if(opt==2){
change(l,r,1,2);
}
else{
if(opt==3) cout<<query(l,r,1).w<<"\n";
else if(opt==4) cout<<query(l,r,1).mw<<"\n";
}
}
return 0;
}

P4979 矿洞:坍塌 蓝

线段树入门题。维护一个颜色就可以了,1/2/3 分别是全A/B/C,-1 表示混色。

最后查询不用直接返回合不合法,如果这个区间是纯色那么返回区间的颜色就可以了,否则返回 -1

判断合法的条件就是 [l,r] 返回值不为 -1 且 [l-1,l-1],[r+1,r+1] 不相同。有两种情况例外,l=1 或者 r=n 。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
char s[N];
int a[N],n,k;
struct node{
int l,r,col,add;
}t[N*4];
void push_up(int p){
if(t[p*2].col==t[p*2+1].col) t[p].col=t[p*2].col;
else t[p].col=-1;
}
void push_down(int p){
if(t[p].add!=-1){
t[p*2].col=t[p].add;t[p*2+1].col=t[p].add;
t[p*2].add=t[p].add;t[p*2+1].add=t[p].add;
t[p].add=-1;
}
}
void build(int l,int r,int p){
t[p].l=l;t[p].r=r;t[p].add=-1;
if(l==r){
t[p].col=a[l];
return ;
}
int mid=(l+r)>>1;
build(l,mid,p*2);build(mid+1,r,p*2+1);push_up(p);
}
void change(int l,int r,int p,int k){
// cout<<l<<" "<<r<<" "<<p<<" "<<k<<endl;
if(l<=t[p].l and t[p].r<=r){
t[p].add=k;t[p].col=k;
push_down(p);
return ;
}
push_down(p);
int mid=(t[p].l+t[p].r)>>1;
if(l<=mid) change(l,r,p*2,k);
if(r>mid) change(l,r,p*2+1,k);
push_up(p);
}
int query(int l,int r,int p){
if(l==0 or r==n+1) return 0;
if(l<=t[p].l and t[p].r<=r) return t[p].col;
int mid=(t[p].l+t[p].r)>>1;
push_down(p);
int col1=0,col2=0;
if(l<=mid) col1=query(l,r,p*2);
if(r>mid) col2=query(l,r,p*2+1);
if(col1==0) return col2;
if(col2==0) return col1;
if(col1==-1 or col2==-1) return -1;
if(col1==col2) return col1;
return -1;
}
int main(){
cin>>n>>(s+1);
for(int i=1;i<=n;i++){a[i]=s[i]-'A'+1;}
build(1,n,1);
cin>>k;
while(k--){
char opt,op;int x,y;
cin>>opt>>x>>y;
if(opt=='B'){
int fr=query(x-1,x-1,1);int bc=query(y+1,y+1,1);
int ret=query(x,y,1);
// cout<<ret<<" "<<fr<<" "<<bc<<endl;
if(ret==-1) cout<<"No\n";
else if(ret!=-1 and (fr!=bc or x==1 or y==n)) cout<<"Yes\n";
else cout<<"No\n";
}
if(opt=='A'){cin>>op;change(x,y,1,op-'A'+1);}
}
return 0;
}

本文作者:zplqwq

本文链接:https://www.cnblogs.com/zplqwq/p/17144367.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   zplqwq  阅读(77)  评论(0编辑  收藏  举报
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示