$A$:
nt题,不说了。
#include<bits/stdc++.h> #define maxn 200005 #define maxm 500005 #define inf 0x7fffffff #define ll long long #define rint register ll #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } int main(){ ll T=read(); while(T--){ ll a=read(),b=read(),n=read(),ans=0; while(1){ if(a<b) swap(a,b); if(a>n) break; b+=a,ans++; } printf("%lld ",ans); } return 0; }
$B$:
nt题,不说了。
#include<bits/stdc++.h> #define maxn 200005 #define maxm 500005 #define inf 0x7fffffff #define ll long long #define rint register ll #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; ll siz[maxn]; char alp[10]={'c','o','d','e','f','o','r','c','e','s'}; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline ll power(ll a,ll b){ ll ans=1; while(b){ if(b&1) ans=ans*a; a=a*a,b>>=1; } return ans; } int main(){ ll k=read(),n=1; if(k==1){printf("codeforces");return 0;} while(power(n,10)<k) n++; ll res=power(n-1,10); for(ll i=1;i<=10;i++) siz[i]=n-1; for(ll i=1;i<=10;i++){ siz[i]++,res=res/(siz[i]-1)*siz[i]; if(res>=k) break; } for(ll i=1;i<=10;i++) for(ll j=1;j<=siz[i];j++) printf("%c",alp[i-1]); printf(" "); return 0; }
$C$:
nt题,不说了。
#include<bits/stdc++.h> #define maxn 200005 #define maxm 500005 #define inf 0x7fffffff #define ll long long #define rint register int #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; int tot=0,nxt[8][2]={{0,0},{0,1},{0,2},{1,2},{2,2},{2,1},{2,0},{1,0}}; struct node{int x,y;}res[maxn]; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline void ins(node a){ for(int i=1;i<8;i++) res[++tot]=(node){a.x+nxt[i][0],a.y+nxt[i][1]}; } int main(){ int n=read(); node np=(node){0,0}; res[++tot]=np; ins(np),np=(node){np.x+2,np.y+2}; for(int i=1;i<=n;i++) ins(np),np=(node){np.x+2,np.y+2}; printf("%d ",tot); for(int i=1;i<=tot;i++) printf("%d %d ",res[i].x,res[i].y); return 0; }
$D$:
题意:
给你一个序列A,你可以随便选若干对$a_i ,a_j$,将这两个数替换成$a_{i} | a_{j}$和$a_{i} & a_{j}$,要求最大化$sum limits_{i=1}^{n}{a_{i}^{2}}$。
题解:
nt题,发现操作的实质就是把两个数各位的1扔到某一边,不改变两个数的和。
那么要求的最大值相当于让序列的方差尽量大,我们直接把所有1扔到序列的一侧即可。
代码:
#include<bits/stdc++.h> #define maxn 200005 #define maxm 500005 #define inf 0x7fffffff #define ll long long #define rint register ll #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; ll A[maxn],B[maxn],siz[maxn]; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } int main(){ ll n=read(); for(ll i=1;i<=n;i++) A[i]=read(); for(ll i=19;i>=0;i--) for(ll j=1;j<=n;j++) siz[i]+=((A[j]>>i)&1); for(ll i=19;i>=0;i--) for(ll j=1;j<=siz[i];j++) B[j]|=(1<<i); ll ans=0; for(ll i=1;i<=n;i++) ans+=(B[i]*B[i]); printf("%lld ",ans); return 0; }
$E$:
题意:
给你一个n个点m条边的有向图,每个点最多有两条出边。
请你构造一种删掉不超过$frac{4}{7}n$个点使得图中不存在长度为2的路径的方案。
$nleq 2 imes 10^5$。
题解:
直接按拓扑序遍历图,记录到达该点的最长路径长度dis,如果dis为2则删掉这个点。
考虑证明,我们按dis值将点分成三类$V_0 ,V_1 ,V_2 $。
由于$V_2$的边全部来自于$V_1$,而每个点最多两条出边,所以有$| V_2 |leq 2| V_1 |$。同理$| V_2 |leq 2| V_1 |leq 4| V_0 |$。
那么有$| V_2 |+| V_1 |+| V_0 | leq 7 |V_0|$,脑补一下发现$| V_2 |leq frac{4}{7}(| V_2 |+| V_1 |+| V_0 |)=frac{4}{7}n$。
真没想到,不是没想到做法,而是没想到证明。
代码:
#include<bits/stdc++.h> #define maxn 200005 #define maxm 500005 #define inf 0x7fffffff #define ll long long #define rint register int #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; int dis[maxn],ans[maxn]; vector<int> in[maxn]; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } int main(){ int T=read(); while(T--){ int n=read(),m=read(); ans[0]=0; for(int i=1;i<=n;i++) in[i].clear(); for(int i=1;i<=m;i++){ int u=read(),v=read(); in[v].push_back(u); } for(int u=1;u<=n;u++){ dis[u]=0; for(int j=0;j<in[u].size();j++) dis[u]=max(dis[u],dis[in[u][j]]+1); if(dis[u]==2) dis[u]=-1,ans[++ans[0]]=u; } printf("%d ",ans[0]); for(int i=1;i<=ans[0];i++) printf("%d ",ans[i]); printf(" "); } return 0; }
$F$:
题意:
这是一道交互题。
有一个长度为n的环,每个位置有一盏灯,初始都是灭的。现在你要和你的憨批朋友进行一场比赛。
每次你任选k盏灯变成开的,然后你的憨批朋友选定一个位置p,将从p开始连续的k盏灯变成灭的。
在每次操作之前你可以选择是否要结束比赛。
你希望最大化结束比赛时亮的灯数目,而你的憨批朋友希望最小化该数目。
假设你们都采用最优策略,请你构造出一种进行比赛的过程。
每次你要输出k和你选择的k盏灯,然后系统会把你的憨批朋友选定的位置p告诉你。
题解:
假设我们已经有了x盏亮的灯,现在需要选定一个k使得x变大。
那么显然操作之后任何亮的连续段长度不能超过k,发现至少有$lceil frac{n}{k} ceil$段。
考虑到每段之间需要用一个灭的隔开,根据$亮+灭= n$的条件可以得到$x+k+lceil frac{n}{k} ceil= n$。
解得$x= n-k-lceil frac{n}{k} ceil+1$(x变大之后的结果)。
也就是说只要选定一个使$n-k-lceil frac{n}{k} ceil+1$最大的k,就可以一直用固定的k操作了。
那么将环每k个标记一下永远不选,然后用个set什么的维护当前灭的位置集合就行了。
注意交互题在每个输出后面需要整一个$fflush(stdout)$。
套路:
- 推式子时:注意用边界卡出取值范围。
代码:
#include<bits/stdc++.h> #define maxn 200005 #define maxm 500005 #define inf 0x7fffffff #define ll long long #define rint register int #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; set<int> s; int ok[maxn]; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline int frac(int a,int b){return (a%b==0)?(a/b):(a/b+1);} int main(){ int n=read(),k=n,x=0; for(int i=1;i<=n;i++) if(x<=n-i-frac(n,i)+1) x=n-i-frac(n,i)+1,k=i; for(int i=1;i<=n;i+=k) ok[min(i+k-1,n)]=-1; for(int i=1;i<=n;i++) if(ok[i]!=-1) s.insert(i); //cout<<x<<endl; while(s.size()>=k){ printf("%d",k); for(int i=1;i<=k;i++){ auto it=s.begin(); printf(" %d",*it),s.erase(it); } printf(" "),fflush(stdout); int p=read(); if(p==-1) exit(0); for(int i=1;i<=k;i++,p=p%n+1) if(ok[p]!=-1) s.insert(p); } printf("0 "),fflush(stdout); return 0; }
$G$:
题意:
有一个$n imes m$的网格,铺满了多米诺骨牌。你可以任选一个骨牌拿走,然后自由移动剩下的骨牌。
一个骨牌只能沿平行于长边的方向移动,且移动后占的格必须和移动前占的格有交(只能移动一次)。
求有多少种拿走并移动的方案,两种方案不同当且仅当最后棋盘上空的两个格不同。
$nmleq 10^{5}$。
题解:
考虑一个直观的想法:如果v骨牌动了之后u能动,那么连边$(u,v)$。
但这样直接连边不能确定移动方向,导致一条路径不一定能从头移动到尾。
考虑到一般铺骨牌的问题都可以把原图黑白染色后处理,而且这个题最后空出来的格一定是一黑一白。
于是我们将这张图黑白染色,在两个同色格之间按上述方法连边。每条边$(u,v)$表示如果v空出来了,那么移动一次后u能空出来。
容易发现此时每个点最多有一条出边,且有一个结论:此时连出来的必然是一个森林。
(证明:考虑在这个图里随便拿骨牌拼一个环,手画一下发现环内一定有奇数个点,所以这种方案不合法)
那么现在可以考虑计算答案了。发现一对黑白点$(u,v)$能作为答案当且仅当它们分别能走到$(w1,w2)$,满足w1与w2被同一个骨牌覆盖。
翻译一下:有两棵树,求有几对点对$(u,v)$,满足u在第一棵树上的某个祖先与v在第二棵树上的某个祖先相同。
这是个比较简单的数据结构问题,考虑第一棵树上的点u在第二棵树上有多少个合法的v,容易发现就是u所有祖先在第二棵树上的子树的并。
那么实际上就是维护区间加减和区间非零的个数,直接用线段树维护区间最小值和最小值个数即可。
复杂度$O(nmlog{nm})$。
套路:
- 网格图上只与相邻两格有关的问题$ ightarrow$黑白染色。
- 构造了一个图但是没什么性质$ ightarrow$改成一棵树。
代码:
#include<bits/stdc++.h> #define maxn 200005 #define maxm 500005 #define inf 0x7fffffff #define ll long long #define rint register int #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; int fa[2][maxn],to[2][maxn],nxt[2][maxn],hd[2][maxn]; int dfn[2][maxn],P[2][maxn],siz[2][maxn],num[2]; int trmn[maxn<<2],trsz[maxn<<2],trlz[maxn<<2],cnt[2]; char str[maxn]; map<int,int> id[maxn]; ll ans; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline void addedge(int u,int v,int op){fa[op][u]=v,to[op][++cnt[op]]=u,nxt[op][cnt[op]]=hd[op][v],hd[op][v]=cnt[op];} inline void dfs(int u,int op){ dfn[op][u]=++num[op],siz[op][u]=1; for(int i=hd[op][u];i;i=nxt[op][i]) {int v=to[op][i];dfs(v,op);siz[op][u]+=siz[op][v];} } inline void pushup(int k){ if(trmn[k<<1]==trmn[k<<1|1]) trmn[k]=trmn[k<<1],trsz[k]=trsz[k<<1]+trsz[k<<1|1]; else if(trmn[k<<1]<trmn[k<<1|1]) trmn[k]=trmn[k<<1],trsz[k]=trsz[k<<1]; else trmn[k]=trmn[k<<1|1],trsz[k]=trsz[k<<1|1]; } inline void pushdown(int k){ if(trlz[k]){ int tp=trlz[k]; trlz[k]=0; trlz[k<<1]+=tp,trlz[k<<1|1]+=tp; trmn[k<<1]+=tp,trmn[k<<1|1]+=tp; } } inline void build(int l,int r,int k){ trmn[k]=0,trsz[k]=r-l+1; if(l==r) return; int mid=l+r>>1; build(l,mid,k<<1),build(mid+1,r,k<<1|1); } inline void add(int x,int y,int z,int l,int r,int k){ if(x<=l && r<=y){trmn[k]+=z,trlz[k]+=z;return;} pushdown(k); int mid=l+r>>1; if(x<=mid) add(x,y,z,l,mid,k<<1); if(y>mid) add(x,y,z,mid+1,r,k<<1|1); pushup(k); } inline void Dfs(int u){ add(dfn[1][u],dfn[1][u]+siz[1][u]-1,1,1,num[1],1); ans+=(ll)(num[0]-((trmn[1]==0)?trsz[1]:0)); for(int i=hd[0][u];i;i=nxt[0][i]) Dfs(to[0][i]); add(dfn[1][u],dfn[1][u]+siz[1][u]-1,-1,1,num[1],1); } int main(){ int n=read(),m=read(),tot=0; for(int i=1;i<=n;i++){ scanf("%s",str+1); for(int j=1;j<=m;j++){ if(str[j]=='L') id[i][j]=id[i][j+1]=++tot; if(str[j]=='U') id[i][j]=id[i+1][j]=++tot; } } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ if(id[i][j]==id[i][j+1]){ if((i+j)&1){ if(j+2<=m) addedge(id[i][j],id[i][j+2],1); if(j-1>=1) addedge(id[i][j+1],id[i][j-1],0); } else{ if(j+2<=m) addedge(id[i][j],id[i][j+2],0); if(j-1>=1) addedge(id[i][j+1],id[i][j-1],1); } } if(id[i][j]==id[i+1][j]){ if((i+j)&1){ if(i+2<=n) addedge(id[i][j],id[i+2][j],1); if(i-1>=1) addedge(id[i+1][j],id[i-1][j],0); } else{ if(i+2<=n) addedge(id[i][j],id[i+2][j],0); if(i-1>=1) addedge(id[i+1][j],id[i-1][j],1); } } } for(int k=0;k<2;k++) for(int i=1;i<=tot;i++) if(!fa[k][i]) dfs(i,k); build(1,num[1],1); for(int i=1;i<=tot;i++) if(!fa[0][i]) Dfs(i); printf("%lld ",ans); return 0; }
$H$:
题意:
给你一个电路板,上下各m个接口,左右各n个接口,中间有$n imes m$个节点。每个接口为红色或蓝色。
你希望用若干条导线连接这些接口,满足:
- 导线两端点必须连一个红色接口和一个蓝色接口,每个接口最多连一条导线。
- 导线的任何一段必须是竖直或平行的,且只能在节点处拐弯。
- 任何两根导线都不能有公共长度,但可以有公共节点。
现在有q次操作,每次操作将上/下/左/右位于$[L,R]$之间的接口颜色取反(红变蓝,蓝变红)。
每次操作结束后你需要输出此时的电路板最多可以连接多少根导线。
$n,m,qleq 10^{5}$。
题解:
显然如果没有数据范围要求的话可以直接连边跑最大流。
由于最大流等于最小割,相当于求最少割掉多少条边使得红/蓝不连通。
考虑一条切割路径,如果它拐弯了,那么总存在一种方式将它替换成一个整行/整列的切割和若干个在端点处的切割,且替换后不会增加答案。
那么这张图的最小割之一就是由一堆整行/整列的切割和一堆端点处的切割组成的。(注意有整行切割就不能有整列切割,反之亦然)
此时我们就可以弄两个线性dp来计算答案了,大概是$dp(i,0/1)$表示切割完前i个且前面传过来的颜色是红/蓝的最小代价。
考虑支持修改,由于这个dp是区间合并的形式,我们可以用线段树来维护它。
每个线段树节点$tr(l,r,0/1,0/1)$表示将区间$[l,r]$切割完,左边/右边传出去的颜色是红/蓝的最小代价。
由于修改是区间取反的形式,我们还需要加一维表示(以上下为例)上下都不取反/上取反/下取反/上下都取反的答案。
常数巨大,比较难写。复杂度$O(qlog{(n+m)})$。
套路:
- 网格图最小割$ ightarrow$可以dp算。
- 形如区间合并的dp$ ightarrow$直接用线段树维护。
代码:
#include<bits/stdc++.h> #define maxn 100005 #define maxm 500005 #define inf 0x7fffffff #define ll long long #define rint register int #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; int tot,S; char A[2][maxn],B[2][maxn]; char str[maxn],str0[2][maxn],str1[2][maxn]; struct SegmentTree{ ll tr[maxn<<2][4][2][2],lz[maxn<<2]; inline void rev(int k,int lz){ for(rint i=0;i<2;i++) for(rint j=0;j<2;j++){ if(lz==1) swap(tr[k][0][i][j],tr[k][1][i][j]),swap(tr[k][2][i][j],tr[k][3][i][j]); if(lz==2) swap(tr[k][0][i][j],tr[k][2][i][j]),swap(tr[k][1][i][j],tr[k][3][i][j]); if(lz==3) swap(tr[k][0][i][j],tr[k][3][i][j]),swap(tr[k][1][i][j],tr[k][2][i][j]); } } inline void pushup(ll k){ for(rint t=0;t<4;t++) for(rint l=0;l<2;l++) for(rint r=0;r<2;r++){ tr[k][t][l][r]=1ll<<61; for(rint lc=0;lc<2;lc++) for(rint rc=0;rc<2;rc++) tr[k][t][l][r]=min(tr[k][t][l][r],tr[k<<1][t][l][lc]+tr[k<<1|1][t][rc][r]+((lc!=rc)?tot:0)); } } inline void pushdown(int k){ if(lz[k]){ lz[k<<1]^=lz[k],lz[k<<1|1]^=lz[k]; rev(k<<1,lz[k]),rev(k<<1|1,lz[k]),lz[k]=0; } } inline void build(int l,int r,int k){ if(l==r){ ll m=S-tot; if(l==0){ ll n=tot,res=0; for(int i=1;i<=n;i++) res+=(str1[0][i]=='B'); for(int i=0;i<4;i++) tr[k][i][0][0]=n-res,tr[k][i][1][1]=res; } else if(l==m+1){ ll n=tot,res=0; for(int i=1;i<=n;i++) res+=(str1[1][i]=='B'); for(int i=0;i<4;i++) tr[k][i][0][0]=n-res,tr[k][i][1][1]=res; } else{ int t1=(str0[0][l]=='B'),t2=(str0[1][l]=='B'); tr[k][0][0][0]=(t1^1)+(t2^1),tr[k][0][1][1]=t1+t2; tr[k][1][0][0]=t1+(t2^1),tr[k][1][1][1]=(t1^1)+t2; tr[k][2][0][0]=(t1^1)+t2,tr[k][2][1][1]=t1+(t2^1); tr[k][3][0][0]=t1+t2,tr[k][3][1][1]=(t1^1)+(t2^1); } return; } int mid=l+r>>1; build(l,mid,k<<1),build(mid+1,r,k<<1|1); pushup(k); } inline void upd0(int x,int y,int l,int r,int k){ if(l==r){ for(rint i=0;i<4;i++) tr[k][i][1][1]+=y,tr[k][i][0][0]=tot-tr[k][i][1][1]; return; } pushdown(k); int mid=l+r>>1; if(x<=mid) upd0(x,y,l,mid,k<<1); else upd0(x,y,mid+1,r,k<<1|1); pushup(k); } inline void upd1(int x,int y,int z,int l,int r,int k){ if(x<=l && r<=y){lz[k]^=z,rev(k,z);return;} pushdown(k); int mid=l+r>>1; if(x<=mid) upd1(x,y,z,l,mid,k<<1); if(y>mid) upd1(x,y,z,mid+1,r,k<<1|1); pushup(k); } }trn,trm; struct SegmentTree_au{ ll tr[maxn<<2],lz[maxn<<2]; inline void pushdown(int k,int l){ if(lz[k]){ int rl=l>>1; lz[k]=0; lz[k<<1]^=1,tr[k<<1]=l-rl-tr[k<<1]; lz[k<<1|1]^=1,tr[k<<1|1]=rl-tr[k<<1|1]; } } inline void build(int l,int r,int k){ if(l==r){tr[k]=(str[l]=='B');return;} int mid=l+r>>1; build(l,mid,k<<1),build(mid+1,r,k<<1|1); tr[k]=tr[k<<1]+tr[k<<1|1]; } inline void upd(int x,int y,int l,int r,int k){ if(x<=l && r<=y){tr[k]=r-l+1-tr[k],lz[k]^=1;return;} pushdown(k,r-l+1); int mid=l+r>>1; if(x<=mid) upd(x,y,l,mid,k<<1); if(y>mid) upd(x,y,mid+1,r,k<<1|1); tr[k]=tr[k<<1]+tr[k<<1|1]; } }L,R,U,D; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } int main(){ memset(trm.tr,63,sizeof(trm.tr)); memset(trn.tr,63,sizeof(trn.tr)); ll n=read(),m=read(),q=read(),ans=1ll<<62; S=n+m; scanf("%s",A[0]+1),scanf("%s",A[1]+1); scanf("%s",B[0]+1),scanf("%s",B[1]+1); memcpy(str1,A,sizeof(A)),memcpy(str0,B,sizeof(B)); tot=n,trm.build(0,m+1,1); memcpy(str0,A,sizeof(A)),memcpy(str1,B,sizeof(B)); tot=m,trn.build(0,n+1,1); memcpy(str,A[0],sizeof(A[0])),L.build(1,n,1); memcpy(str,A[1],sizeof(A[1])),R.build(1,n,1); memcpy(str,B[0],sizeof(B[0])),U.build(1,m,1); memcpy(str,B[1],sizeof(B[1])),D.build(1,m,1); for(rint i=0;i<2;i++) for(rint j=0;j<2;j++) ans=min(ans,min(trn.tr[1][0][i][j],trm.tr[1][0][i][j])); printf("%lld ",ans); while(q--){ char ch; cin>>ch; int l=read(),r=read(); ans=1ll<<62; if(ch=='L'){ int res=-L.tr[1];L.upd(l,r,1,n,1),res+=L.tr[1]; tot=n,trm.upd0(0,res,0,m+1,1); tot=m,trn.upd1(l,r,1,0,n+1,1); } if(ch=='R'){ int res=-R.tr[1];R.upd(l,r,1,n,1),res+=R.tr[1]; tot=n,trm.upd0(m+1,res,0,m+1,1); tot=m,trn.upd1(l,r,2,0,n+1,1); } if(ch=='U'){ int res=-U.tr[1];U.upd(l,r,1,m,1),res+=U.tr[1]; tot=m,trn.upd0(0,res,0,n+1,1); tot=n,trm.upd1(l,r,1,0,m+1,1); } if(ch=='D'){ int res=-D.tr[1];D.upd(l,r,1,m,1),res+=D.tr[1]; tot=m,trn.upd0(n+1,res,0,n+1,1); tot=n,trm.upd1(l,r,2,0,m+1,1); } for(rint i=0;i<2;i++) for(rint j=0;j<2;j++) ans=min(ans,min(trn.tr[1][0][i][j],trm.tr[1][0][i][j])); printf("%lld ",ans); } return 0; }