回归——Tj1
csp-s2019考的不是很好,D1还可以,210`,D2就GG了(据说有洛谷黑题),然后只有80+(本来有一个O(n^2)的dp都没写,自己的贪心是错了)
总共才289是不是很菜啊。。。
但是出来一看竟然不算太低,打算冲击省队了(heiheihei)
好久没法博客了,感觉过去了一个世纪,自己最近一直在补知识点(平衡树,各种流,点分治,数据结构。。。)
总算补齐了,开始giao数学
1.多项式
定义,基本性质。。。(自行百度)
Duan2baka dalao的讲解十分细致,请翻转
https://blog.csdn.net/WADuan2/article/details/79529900
FFT:快速傅里叶变换,听起来好牛逼的亚子
原理:通过复数的性质实现快速插值,最后利用复数的性质求值的过程
具体步骤和模板:见上文博客
下面讲解最关键的部分:
蝴蝶变换(迭代写法)
目前比较流行的写法有两种:
1.递推法:
1 for(int i=1;i<n-1;i++){ 2 rev[i]=(rev[i>>1]>>1)|((i&1)&(n>>1)); 3 }
2.名字忘了:
1 inline void rader(Complex a[],int len){//二进制翻转 2 int j=len>>1; 3 for(int i=1;i<len-1;i++){ 4 if(i<j)swap(a[i],a[j]); 5 int k=len>>1; 6 while(j&k){ 7 j^=k; 8 k>>=1; 9 } 10 j|=k;//位运算便于理解 11 } 12 }
本蒟蒻当初学的时候就是看不懂蝴蝶变换,甚至背了板子
那么好,蝴蝶变化是干什么的?
在写FFT的 时候,大家应该从递归版本开始写,就会发现,你传入的是系数,返回的就成了插入(n/2)次单位根的值时的点值(!!!Rechardluan 一开始也懵逼),所以上面图片中只有最底下的step3中的W是有意义的,又因为到最后的时候n=1,Wn=1,所以点值直接就是系数了,
至于为什么这样是对的,考虑当前的系数,递归时,把偶数按次放左边,奇数按次放右边
实际上就是二进制的最后一位为0(左边)或者为1(右边)
之后的过程相当于将所有的数 >>1 比较二进制下的第2位
到最后就会出现二进制翻转的情况了
所以蝴蝶变换本质上就是递归过程的加快,没什么特殊的性质
完整代码一只:
1 #include<bits/stdc++.h> 2 using namespace std; 3 char C_[50]; 4 int TEMP; 5 template<typename T> 6 inline void write(T a){ 7 if(a<0){ 8 putchar('-'); 9 a=-a; 10 } 11 do{ 12 C_[++TEMP]=a%10+'0'; 13 a/=10; 14 }while(a); 15 while(TEMP)putchar(C_[TEMP--]); 16 putchar(' '); 17 } 18 const int maxn=1e7+5; 19 const long double pi=acos(-1); 20 struct Complex{ 21 double r,i; 22 Complex(double R=0.0,double I=0.0):r(R),i(I){} 23 Complex operator+(const Complex&C)const{ 24 Complex ans; 25 ans.r=r+C.r; 26 ans.i=i+C.i; 27 return ans; 28 } 29 Complex operator-(const Complex&C)const{ 30 Complex ans; 31 ans.r=r-C.r; 32 ans.i=i-C.i; 33 return ans; 34 } 35 Complex operator*(const Complex&C)const{ 36 Complex ans; 37 ans.r=r*C.r-i*C.i; 38 ans.i=r*C.i+i*C.r; 39 return ans; 40 } 41 }a[maxn],b[maxn]; 42 int rev[maxn]; 43 inline void rader(Complex a[],int len){//二进制翻转 44 int j=len>>1; 45 for(int i=1;i<len-1;i++){ 46 if(i<j)swap(a[i],a[j]); 47 int k=len>>1; 48 while(j&k){ 49 j^=k; 50 k>>=1; 51 } 52 j|=k;//位运算便于理解 53 } 54 } 55 inline void FFT(Complex a[],int len,int v){ 56 //rader(a,len); 57 for(int i=1;i<len-1;i++)if(i<rev[i])swap(a[i],a[rev[i]]); 58 59 for(int l=2,mid=1;l<=len;mid<<=1,l<<=1){ 60 //mid是当前长度的一半 l是当前长度 61 Complex Wn=Complex(cos(2.0*pi/l),v*sin(2.0*pi/l));//n次单位根 62 for(int i=0;i<len;i+=l){ 63 Complex W=Complex(1.0,0.0); 64 for(int j=i;j<i+mid;j++,W=W*Wn){ 65 Complex A1=a[j],A2=W*a[j+mid]; 66 a[j]=A1+A2; 67 a[j+mid]=A1-A2; 68 } 69 } 70 } 71 } 72 int n,m; 73 int main(){ 74 scanf("%d%d",&n,&m);n++,m++; 75 for(int i=0;i<n;i++)scanf("%lf",&a[i].r); 76 for(int i=0;i<m;i++)scanf("%lf",&b[i].r); 77 int len=1,L=0; 78 n=n+m-1;//项数 79 while(len<=n)len<<=1,L++;//保证2^n项 80 for(int i=1;i<len-1;i++){ 81 rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1)); 82 } 83 FFT(a,len,1.0); 84 FFT(b,len,1.0);//转化成点值表示 85 for(int i=0;i<len;i++)a[i]=a[i]*b[i];//相乘 86 87 FFT(a,len,-1.0);//INFT 插值 88 for(int i=0;i<n;i++)write(int(a[i].r/len+0.5)); 89 return 0; 90 }
例题的话不太多,FFT加速字符串匹配?
基本推出式子就可以了
NTT:差不多吧。把复数单位根换成原根
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define rep(i,l,r) for(int i=(l);i<=(r);i++) 4 using namespace std; 5 const LL P=998244353ll; 6 const int maxn=1e6; 7 const LL g=3ll; 8 const LL invg=332748118ll; 9 inline LL quickpow(LL a,LL n){ 10 LL ans=1ll; 11 while(n){ 12 if(n&1)ans=ans*a%P; 13 a=a*a%P; 14 n>>=1; 15 } 16 return ans%P; 17 } 18 int n,m,len,L,rev[maxn<<2]; 19 LL a[maxn<<2],b[maxn<<2]; 20 inline void NTT(LL a[],int len,int b){ 21 rep(i,0,len-1)if(i<rev[i])swap(a[i],a[rev[i]]); 22 for(int mid=1,l=2;l<=len;l<<=1,mid<<=1){ 23 LL Wn=quickpow( b==1 ? g : invg , (P-1)/l); 24 for(int i=0;i<len;i+=l){ 25 LL W=1; 26 for(int j=i;j<i+mid;j++,W=(W*Wn)%P){ 27 LL A1=a[j]%P,A2=W*a[j+mid]%P; 28 a[j]=(A1+A2)%P; 29 a[j+mid]=((A1-A2)%P+P)%P; 30 } 31 } 32 } 33 } 34 int main(){ 35 scanf("%d%d",&n,&m); 36 rep(i,0,n)scanf("%lld",&a[i]);//n次多项式 37 rep(i,0,m)scanf("%lld",&b[i]);//m次多项式 38 len=1;L=0; 39 while(len<n+m+1){//n+m次多项式 [0,n+m] 40 len<<=1; 41 L++; 42 } 43 rep(i,0,len-1)rev[i]=( (rev[i>>1]>>1) | ((i&1)<<(L-1)) ); 44 NTT(a,len,1); 45 NTT(b,len,1); 46 rep(i,0,len-1)a[i]=a[i]*b[i]%P; 47 NTT(a,len,-1); 48 LL invl=quickpow(1ll*len,P-2); 49 rep(i,0,(n+m))printf("%lld ",a[i]*invl%P); 50 return 0; 51 }
ps:自己真的好懒,很多东西都没讲,Duan2baka讲的挺详细的(我会告诉你们我不会用markdown编辑器么)
平衡树:这东西就像segtree一样,是个工具,用于处理 区间问题,然后自己总结了一下板子,以供参考
1 #include<bits/stdc++.h> 2 using namespace std; 3 //splay 4 const int maxn=1e5+5; 5 int n,m,root,tot; 6 struct Node{ 7 int val,lazy,ch[2],size,fa; 8 Node(){val=lazy=ch[0]=ch[1]=size=fa=0;} 9 inline void modify(){ 10 swap(ch[0],ch[1]); 11 lazy^=1; 12 } 13 }t[maxn]; 14 inline int drc(int x){//0->leftson,1->rightson 15 if(t[x].fa) return x == t[ t[x].fa ].ch[1] ; 16 else return -1; 17 } 18 inline void down(int x){ 19 if(t[x].lazy){ 20 if(t[x].ch[0])t[t[x].ch[0]].modify(); 21 if(t[x].ch[1])t[t[x].ch[1]].modify(); 22 t[x].lazy=0; 23 } 24 } 25 inline void maintain(int x){if(!x)return;t[x].size=t[t[x].ch[0]].size+t[t[x].ch[1]].size+1;} 26 inline void rtt(int &x){//将x上旋 27 int f=t[x].fa;int b=drc(x); 28 t[f].ch[b]=t[x].ch[b^1];t[x].ch[b^1]=f; 29 t[x].fa=t[f].fa; 30 if(drc(f)!=-1)t[t[f].fa].ch[drc(f)]=x; 31 if(t[f].ch[b])t[ t[f].ch[b] ].fa=f; 32 t[f].fa=x; 33 maintain(f);maintain(x); 34 } 35 inline void splay(int x,int &target){ 36 while(t[x].fa && x!=target){//还不是根 37 if(t[x].fa!=target && drc(x)==drc(t[x].fa) )//能构成3点1线 38 rtt(t[x].fa); 39 else rtt(x); 40 } 41 target=x; 42 } 43 void build(int &k,int fa,int l,int r){ 44 if(l>r)return; 45 46 k=++tot; 47 t[k].fa=fa; 48 t[k].val=(l+r)>>1; 49 50 build(t[k].ch[0],k,l,t[k].val-1); 51 build(t[k].ch[1],k,t[k].val+1,r); 52 53 maintain(k); 54 } 55 inline int rank_k(int k){//找到第k大的位置 56 int x=root; 57 while(x){ 58 down(x); 59 60 if(t[t[x].ch[0]].size+1==k)return x; 61 bool b=t[t[x].ch[0]].size+1 <= k; 62 if(b)k-=(t[t[x].ch[0]].size+1); 63 x=t[x].ch[b]; 64 } 65 } 66 void print(int x){ 67 if(!x)return; 68 down(x); 69 70 print(t[x].ch[0]); 71 if(1<=t[x].val && t[x].val<=n )printf("%d ",t[x].val); 72 print(t[x].ch[1]); 73 } 74 int main(){ 75 scanf("%d%d",&n,&m); 76 build(root,0,0,n+1); 77 while(m--){ 78 int l,r; 79 scanf("%d%d",&l,&r); 80 l=rank_k(l);splay(l,root); 81 r=rank_k(r+2);splay(r,t[root].ch[1]); 82 t[ t[r].ch[0] ].modify(); 83 } 84 print(root); 85 return 0; 86 } 87 //无旋treap 88 const int maxn=1e5+5; 89 struct Node{ 90 int val,rd,ch[2],lazy,size; 91 Node(){val=rd=ch[1]=ch[0]=lazy=size=0;} 92 inline void over(){ 93 lazy^=1; 94 swap(ch[0],ch[1]); 95 } 96 }t[maxn]; 97 int root,tot; 98 inline void New(int &x,int val){ 99 x=++tot; 100 t[x].rd=rand(); 101 t[x].val=val; 102 t[x].size=1; 103 } 104 inline void down(int x){ 105 if(t[x].lazy){ 106 if(t[x].ch[0])t[t[x].ch[0]].over(); 107 if(t[x].ch[1])t[t[x].ch[1]].over(); 108 t[x].lazy=0; 109 } 110 } 111 inline void maintain(int x){t[x].size=t[t[x].ch[0]].size+t[t[x].ch[1]].size+1;} 112 void split(int x,int k,int &r1,int &r2){ 113 if(!x){r1=r2=0;return;} 114 115 down(x); 116 117 if(k<=t[t[x].ch[0]].size){ 118 r2=x; 119 split(t[x].ch[0],k,r1,t[x].ch[0]); 120 } 121 else{ 122 r1=x; 123 split(t[x].ch[1],k-t[t[x].ch[0]].size-1,t[x].ch[1],r2); 124 } 125 maintain(x); 126 } 127 int merge(int r1,int r2){ 128 if(!r1 || !r2)return r1+r2; 129 if(t[r1].rd<t[r2].rd){ 130 131 down(r1); 132 133 t[r1].ch[1]=merge(t[r1].ch[1],r2); 134 maintain(r1); 135 return r1; 136 } 137 else{ 138 139 down(r2); 140 141 t[r2].ch[0]=merge(r1,t[r2].ch[0]); 142 maintain(r2); 143 return r2; 144 } 145 } 146 void print(int x){//中序遍历 147 if(!x)return; 148 149 down(x); 150 151 print(t[x].ch[0]); 152 printf("%d ",t[x].val); 153 print(t[x].ch[1]); 154 } 155 int n,m; 156 int main(){ 157 srand(time(0)); 158 159 scanf("%d%d",&n,&m); 160 for(int i=1,rt;i<=n;i++){ 161 New(rt,i); 162 root=merge(root,rt); 163 //O(??)构建treap 164 } 165 for(int i=1,l,r;i<=m;i++){ 166 scanf("%d%d",&l,&r); 167 int r1,r2,r3; 168 split(root,r,r2,r3);//[1~r][r+1~n] 169 split(r2,l-1,r1,r2);//[1~l-1][l~r][r+1~n] 170 t[r2].over(); 171 root=merge(merge(r1,r2),r3); 172 } 173 print(root); 174 return 0; 175 } 176 //完整版 带旋treap 177 #include<bits/stdc++.h> 178 using namespace std; 179 const int maxn=3e5+5; 180 const int INF=1e9; 181 int tot,root; 182 struct Node { 183 int val,size,cnt,to[2],key; 184 Node(){ 185 size=cnt=0; 186 to[0]=to[1]=0; 187 key=rand(); 188 } 189 }t[maxn]; 190 inline void update(int x){ 191 t[x].size=t[ t[x].to[0] ].size+t[ t[x].to[1] ].size+t[x].cnt; 192 } 193 inline void rtt(int &x,bool b){ 194 //作为父亲的节点 旋转方向 195 int s=t[x].to[b]; 196 t[x].to[b]=t[s].to[b^1]; 197 t[s].to[b^1]=x; 198 update(x);update(x=s);//维护节点信息,并且转移父子关系 199 } 200 void insert(int &x,int val){ 201 if(!x){ 202 x=++tot; 203 t[x].size=t[x].cnt=1; 204 t[x].to[0]=t[x].to[1]=0; 205 t[x].val=val; 206 return; 207 } 208 t[x].size++; 209 if(t[x].val==val){ 210 t[x].cnt++; 211 return; 212 } 213 bool b=(val>t[x].val);insert(t[x].to[b],val); 214 215 if(t[ t[x].to[b] ].key>t[x].key)rtt(x,b);//维护一个小跟堆 216 } 217 void del(int &x,int val){ 218 if(!x)return; 219 220 if(t[x].val==val){ 221 if(t[x].cnt>1){ 222 t[x].cnt--; 223 t[x].size--; 224 return; 225 } 226 bool b=(t[ t[x].to[0] ].key>t[ t[x].to[1] ].key); 227 228 if(!t[x].to[0] || !t[x].to[1])x=t[x].to[0]+t[x].to[1]; 229 else{ 230 rtt(x,b); 231 del(x,val); 232 } 233 } 234 else{ 235 t[x].size--; 236 del(t[x].to[ val > t[x].val ],val); 237 } 238 } 239 int rank(int val){ 240 int x=root,ans=0; 241 while(x){ 242 if(t[x].val==val){ 243 ans+=t[ t[x].to[0] ].size+1; 244 break; 245 } 246 bool b=(val>t[x].val); 247 if(b)ans+=t[ t[x].to[0] ].size+t[x].cnt; 248 x=t[x].to[b]; 249 } 250 return ans; 251 } 252 int pre(int val){ 253 int ans=-INF,x=root; 254 while(x){ 255 bool b=(val>t[x].val); 256 if(b)ans=max(ans,t[x].val); 257 x=t[x].to[b]; 258 } 259 return ans== -INF ? -1 : ans ; 260 } 261 int suf(int val){ 262 int ans=INF,x=root; 263 while(x){ 264 bool b=(val>=t[x].val); 265 if(!b)ans=min(ans,t[x].val); 266 x=t[x].to[b]; 267 } 268 return ans== INF ? -1 : ans ; 269 } 270 /*int kth(int k){ 271 int x=root; 272 while(x){ 273 274 if(t[ t[x].to[0] ].size+1==k)return t[x].val; 275 bool b=(k>t[ t[x].to[0] ].size+t[x].cnt); 276 if(b)k-=(t[ t[x].to[0] ].size+t[x].cnt); 277 x=t[x].to[b]; 278 } 279 }*/ 280 int kth(int k){ 281 int x=root; 282 while(1){ 283 if(k<=t[ t[x].to[0] ].size)x=t[x].to[0]; 284 else if(k>t[ t[x].to[0] ].size+t[x].cnt){ 285 k-=t[ t[x].to[0] ].size+t[x].cnt; 286 x=t[x].to[1]; 287 } 288 else return t[x].val; 289 } 290 } 291 int n,op,x; 292 int main(){ 293 srand(time(0)); 294 295 scanf("%d",&n); 296 while(n--){ 297 scanf("%d%d",&op,&x); 298 switch(op){ 299 case 1:{ 300 insert(root,x); 301 break; 302 } 303 case 2:{ 304 del(root,x); 305 break; 306 } 307 case 3:{ 308 printf("%d\n",rank(x)); 309 break; 310 } 311 case 4:{ 312 printf("%d\n",kth(x)); 313 break; 314 } 315 case 5:{ 316 printf("%d\n",pre(x)); 317 break; 318 } 319 case 6:{ 320 printf("%d\n",suf(x)); 321 break; 322 } 323 } 324 } 325 return 0; 326 } 327 //完整版 无旋treap 328 #include<bits/stdc++.h> 329 using namespace std; 330 const int maxn=1e5+5; 331 struct Node{ 332 int val,ls,rs,cnt,size,rd; 333 Node(){val=ls=rs=cnt=size=rd=0;} 334 }t[maxn]; 335 int root,tot; 336 int n,op,x; 337 inline void maintain(int x){t[x].size=t[ t[x].ls ].size+t[ t[x].rs ].size+t[x].cnt;} 338 void split(int x,int val,int &r1,int &r2){ 339 //r1是应该划分到T1中的点,r2是应该划分到T2中的点 340 if(!x){r1=r2=0;return;} 341 if(t[x].val<=val){ 342 r1=x; 343 split(t[x].rs,val,t[x].rs,r2); 344 } 345 else{ 346 r2=x; 347 split(t[x].ls,val,r1,t[x].ls); 348 } 349 350 maintain(x); 351 } 352 void dvd(int x,int k,int &r1,int &r2){ 353 if(!x){r1=r2=0;return;} 354 if(t[ t[x].ls ].size>=k){ 355 r2=x; 356 dvd(t[x].ls,k,r1,t[x].ls); 357 } 358 else{ 359 r1=x; 360 dvd(t[x].rs,k-t[ t[x].ls ].size-t[x].cnt,t[x].rs,r2); 361 } 362 363 maintain(x); 364 } 365 int merge(int r1,int r2){ 366 //合并两棵树 367 if(!r1 || !r2)return r1+r2; 368 if(t[r1].rd<t[r2].rd){ 369 t[r1].rs=merge(t[r1].rs,r2); 370 371 maintain(r1); 372 return r1; 373 } 374 else{ 375 t[r2].ls=merge(r1,t[r2].ls); 376 377 maintain(r2); 378 return r2; 379 } 380 } 381 int End(int x){//查询树x里最大的 节点 382 while(t[x].rs)x=t[x].rs; 383 return x; 384 } 385 int Begin(int x){//查询树x里最小的 节点 386 while(t[x].ls)x=t[x].ls; 387 return x; 388 } 389 void insert(int val){ 390 int r1=0,r2=0,r3=0,r4=0; 391 split(root,val,r1,r2); 392 split(r1,val-1,r3,r4); 393 394 if(t[r4].val==val){ 395 t[r4].cnt++; 396 t[r4].size++; 397 root=merge(merge(r3,r4),r2); 398 } 399 else{ 400 r3=++tot; 401 t[r3].val=val; 402 t[r3].ls=t[r3].rs=0; 403 t[r3].cnt=t[r3].size=1; 404 //注意size赋初值合并的时候不一定能更新 405 t[r3].rd=rand(); 406 407 root=merge(merge(r1,r3),r2); 408 } 409 } 410 void del(int val){ 411 int r1=0,r2=0,r3=0,r4=0; 412 split(root,val,r1,r2); 413 split(r1,val-1,r3,r4); 414 if(t[r4].cnt>1){ 415 t[r4].cnt--; 416 t[r4].size--; 417 root=merge(merge(r3,r4),r2); 418 } 419 else{ 420 root=merge(r3,r2); 421 } 422 } 423 int rank(int val){ 424 int r1=0,r2=0; 425 split(root,val-1,r1,r2); 426 int ans=t[r1].size+1; 427 root=merge(r1,r2); 428 return ans; 429 } 430 int kth(int k){ 431 int r1=0,r2=0; 432 dvd(root,k,r1,r2); 433 int ans=t[End(r1)].val; 434 root=merge(r1,r2); 435 return ans; 436 } 437 int pre(int val){ 438 int r1=0,r2=0; 439 split(root,val-1,r1,r2); 440 int ans = r1 ? t[End(r1)].val : -1 ; 441 root=merge(r1,r2); 442 return ans; 443 } 444 int suf(int val){ 445 int r1=0,r2=0; 446 split(root,val,r1,r2); 447 int ans = r2 ? t[Begin(r2)].val : -1 ; 448 root=merge(r1,r2); 449 return ans; 450 } 451 int main(){ 452 srand(time(0)); 453 454 scanf("%d",&n); 455 while(n--){ 456 scanf("%d%d",&op,&x); 457 switch(op){ 458 case 1:{ 459 insert(x); 460 break; 461 } 462 case 2:{ 463 del(x); 464 break; 465 } 466 case 3:{ 467 printf("%d\n",rank(x)); 468 break; 469 } 470 case 4:{ 471 printf("%d\n",kth(x)); 472 break; 473 } 474 case 5:{ 475 printf("%d\n",pre(x)); 476 break; 477 } 478 case 6:{ 479 printf("%d\n",suf(x)); 480 break; 481 } 482 } 483 } 484 return 0; 485 }
!!!网络流 网络流!!!(无比重要的算法,就是O(能过))
最大流(Dinic)
这东西挺好理解的吧。[NOI2006]最大获利
code:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=5e5+5; 4 const int INF=1e9; 5 int fst[maxn],nxt[maxn],to[maxn],w[maxn],edge_count; 6 inline void add(int x,int y,int z){ 7 edge_count++; 8 to[edge_count]=y; 9 nxt[edge_count]=fst[x]; 10 w[edge_count]=z; 11 fst[x]=edge_count; 12 } 13 inline void insert(int x,int y,int z){add(x,y,z);add(y,x,0);} 14 int S,T,dis[maxn]; 15 queue<int >q; 16 inline bool bfs(){ 17 while(q.size())q.pop(); 18 memset(dis,0,sizeof dis); 19 20 q.push(S); 21 dis[S]=1; 22 while(q.size()){ 23 int u=q.front(); 24 q.pop(); 25 for(int i=fst[u];i;i=nxt[i]){ 26 if(!w[i])continue; 27 int v=to[i]; 28 if(!dis[v]){ 29 dis[v]=dis[u]+1; 30 q.push(v); 31 } 32 } 33 } 34 return dis[T]; 35 } 36 int dfs(int u,int fl){ 37 if(!fl || u==T)return fl; 38 int f,flow=0; 39 for(int i=fst[u];i;i=nxt[i]){ 40 int v=to[i]; 41 if(( dis[v]==dis[u]+1 ) && (f=dfs(v,min(fl,w[i]) ) ) ){ 42 flow+=f; 43 fl-=f; 44 w[i]-=f; 45 w[i^1]+=f; 46 47 if(!fl)break; 48 } 49 } 50 return flow; 51 } 52 int n,m,ans; 53 inline void dinic(){ 54 while(bfs())ans-=dfs(S,INF); 55 } 56 int main(){ 57 edge_count=1; 58 S=maxn-2;T=maxn-3; 59 60 scanf("%d%d",&n,&m); 61 for(int i=1,p;i<=n;i++){ 62 scanf("%d",&p); 63 insert(i,T,p); 64 } 65 for(int i=1,x,y,z;i<=m;i++){ 66 scanf("%d%d%d",&x,&y,&z); 67 ans+=z;//最大权闭合子图 68 insert(S,i+n,z); 69 70 insert(i+n,x,INF); 71 insert(i+n,y,INF); 72 } 73 dinic(); 74 printf("%d",ans); 75 return 0; 76 }
没有当前弧优化。找不到别的板子了。
费用流(原版zkw【注意是原版】,改版zkw,KM)
说一下自己的理解:
Edmond-Karp ,改进之后(用spfa代替bfs)一般的题还是可以的(往往不会卡这个 。吧),然而还是有一道题令我开了O2+O3+O4才过。。。
zkw[original]%zkw,本人blog:https://artofproblemsolving.com/community/c1368h1020435
大致思想说的非常全面(我也不想复制粘贴,毕竟多次翻到同一内容的blog挺烦人的)
至于为什么一直dfs,然后bfs是对的,可能非常玄学吧。
zkw[new]大家好像都写的这个吧。
思路和原版非常像(dinic?),但是是先spfa再增广,不断扩大可行点集,引入新最短路算法:连续最短路。据说可以处理负边权(原版不能)
【在此,我想作为oier发声:希望一些人不要抄袭别人的blog了,学知识点的时候,翻10多篇才有一篇不一样的,剩下的都是一个模子出来的,复制粘贴?为啥不直接把原博文地址贴上?哪怕是点开+关闭也是需要时间的吧,而且心态也炸了,刚刚一直想矫正一下,zkw能不能负边权,结果翻到好多一样的博客,心态炸了,浪费了1h啥都没干,还有,希望大家写板子的时候,弄清到底是哪个算法。有好几个打着zkw的名号写的别的算法。】
T1:SDOI2017 新生舞会
emmmmmm自己写的多路增广(就不瞎jb起名字了,我也不知道是smjb算法),就是spfa求出最短路,dfs实现多路增广(和 ek有区别么?)
code(和dinic几乎完全相同):
1 #include<bits/stdc++.h> 2 #define boy(x) (x<<1) 3 #define girl(x) (x<<1|1) 4 using namespace std; 5 const int maxn=1e2+5; 6 const double inf=10000000000.0; 7 const double eps=1e-6; 8 inline bool equal(double a,double b){return fabs(a-b)<=eps;} 9 int a[maxn][maxn],b[maxn][maxn],n; 10 int cur[maxn<<1],fst[maxn<<1],to[maxn*maxn<<2],nxt[maxn*maxn<<2],w[maxn*maxn<<2],edge_count; 11 double c[maxn*maxn<<2]; 12 inline void add(int x,int y,int flow,double cost){ 13 edge_count++; 14 nxt[edge_count]=fst[x]; 15 fst[x]=edge_count; 16 w[edge_count]=flow; 17 c[edge_count]=cost; 18 to[edge_count]=y; 19 } 20 double maxcost; 21 inline void add_edge(int x,int y,int flow,double cost){add(x,y,flow,cost);add(y,x,0,-cost);} 22 inline void clear(){ 23 memset(fst,0,sizeof fst); 24 edge_count=1; 25 maxcost=0.0; 26 } 27 int S=1,T=(maxn-1)<<1; 28 bool vis[maxn<<1]; 29 double dis[maxn<<1]; 30 queue<int > q; 31 inline bool SPFA(){ 32 for(int i=boy(1);i<=girl(n);i++)dis[i]=-inf,cur[i]=fst[i],vis[i]=0; 33 dis[T]=-inf;cur[T]=fst[T];vis[T]=0; 34 dis[S]=0.0;cur[S]=fst[S]; 35 36 q.push(S);vis[S]=1; 37 while(q.size()){ 38 int u=q.front();q.pop(); 39 vis[u]=0; 40 for(int i=fst[u];i;i=nxt[i]){ 41 int v=to[i]; 42 if(w[i] && ( dis[u]+c[i] > dis [v] ) ){ 43 dis[v]=dis[u]+c[i]; 44 if(!vis[v]){ 45 q.push(v); 46 vis[v]=1; 47 } 48 } 49 } 50 } 51 return !equal(dis[T],-inf); 52 } 53 int dfs(int u,int flow){//死循环原因:标记是否达到 54 if(!flow || u==T)return flow; 55 //sb错误原因:一种写法是整个dfs结束时,mc+=dis[T]*flow 但是有个问题在于,可能进入T点但是不是合法路径(最长路) 56 int fl=0,f;vis[u]=1; 57 for(int &i=cur[u];i;i=nxt[i]){ 58 int v=to[i]; 59 if(vis[v])continue; 60 if( equal(dis[u]+c[i],dis[v])&&( f=dfs(v,min(flow,w[i])) ) ){ 61 //!!!!!!!!逻辑算符 bool f1() && bool f2() 会在f1()=0时,放弃计算f2() 62 w[i]-=f; 63 w[i^1]+=f; 64 fl+=f; 65 flow-=f; 66 maxcost+=c[i]*f; 67 if(!flow)break; 68 } 69 } 70 return fl; 71 } 72 inline void MCMF(){//最大费用最大流 zkw??? 73 while(SPFA()){ 74 dfs(S,n); 75 } 76 } 77 inline bool judge(double ans){ 78 clear(); 79 for(int i=1;i<=n;i++){ 80 add_edge(S,boy(i),1,0.0); 81 add_edge(girl(i),T,1,0.0); 82 for(int j=1;j<=n;j++) 83 add_edge(boy(i),girl(j),1,1.0*a[i][j]-ans*b[i][j]); 84 } 85 MCMF(); 86 return maxcost>=0.0 ; 87 } 88 int main(){ 89 scanf("%d",&n); 90 for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&a[i][j]); 91 for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&b[i][j]); 92 double l=0.0,r=10000.0; 93 double mid=(l+r)/2.0; 94 for(int i=1;i<=40;i++){ 95 if(judge(mid))l=mid; 96 else r=mid; 97 mid=(l+r)/2.0; 98 } 99 printf("%.6lf",l); 100 return 0; 101 }
update:才知道自己写错了。附上正确模板 【luogu P3381】【模板】最小费用最大流
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define rep(i,l,r) for(int i=(l);i<=(r);i++) 4 using namespace std; 5 const int inf=1e9; 6 template<typename T> 7 inline void read(T &a){ 8 a=0;bool b=0;char x=getchar(); 9 while(x<'0'||'9'<x){ 10 if(x=='-')b=1; 11 x=getchar(); 12 } 13 while('0'<=x&&x<='9'){ 14 a=(a<<1)+(a<<3)+x-'0'; 15 x=getchar(); 16 } 17 if(b)a=-a; 18 } 19 char C_[50]; 20 int TEMP; 21 template<typename T> 22 inline void write(T a){ 23 if(a<0){ 24 putchar('-'); 25 a=-a; 26 } 27 do{ 28 C_[++TEMP]=a%10+'0'; 29 a/=10; 30 }while(a); 31 while(TEMP)putchar(C_[TEMP--]); 32 } 33 const int maxn=5005; 34 const int maxm=50005; 35 int cur[maxn],fst[maxn],nxt[maxm<<1],to[maxm<<1],w[maxm<<1],c[maxm<<1],edge_count; 36 inline void add(int x,int y,int flow,int cost){ 37 edge_count++; 38 to[edge_count]=y; 39 w[edge_count]=flow; 40 c[edge_count]=cost; 41 nxt[edge_count]=fst[x]; 42 fst[x]=edge_count; 43 } 44 int S,T; 45 LL dis[maxn]; 46 queue<int >q; 47 bool vis[maxn]; 48 LL maxflow,mincost; 49 inline bool SPFA(){ 50 rep(i,1,maxn-1)dis[i]=inf,vis[i]=0,cur[i]=fst[i]; 51 dis[S]=0;vis[S]=1; 52 q.push(S); 53 while(q.size()){ 54 int u=q.front();q.pop(); 55 vis[u]=0; 56 for(int i=fst[u];i;i=nxt[i]){ 57 int v=to[i]; 58 if(w[i] && dis[v] > dis[u] + c[i]){ 59 dis[v]=dis[u]+c[i]; 60 if(!vis[v]){ 61 vis[v]=1; 62 q.push(v); 63 } 64 } 65 } 66 } 67 return dis[T]<inf; 68 } 69 int dfs(int u,int flow){ 70 if(!flow || u==T)return flow; 71 int fl=0,f;vis[u]=1; 72 for(int &i=cur[u];i;i=nxt[i]){ 73 int v=to[i]; 74 if(!vis[v] && dis[v]==dis[u]+c[i] && ( f=dfs(v,min(flow,w[i] ) ) ) ){ 75 w[i]-=f; 76 w[i^1]+=f; 77 fl+=f; 78 flow-=f; 79 mincost+=f*c[i]; 80 if(!flow)break; 81 } 82 } 83 vis[u]=0;//一定要加的。就像打标记一样 84 return fl; 85 } 86 inline void MCMF(){ 87 while(SPFA())maxflow+=dfs(S,inf); 88 } 89 int n,m; 90 int main(){ 91 edge_count=1; 92 scanf("%d%d%d%d",&n,&m,&S,&T); 93 rep(i,1,m){ 94 int a,b,c,d; 95 scanf("%d%d%d%d",&a,&b,&c,&d); 96 add(a,b,c,d);add(b,a,0,-d); 97 } 98 MCMF(); 99 printf("%lld %lld",maxflow,mincost); 100 return 0; 101 }
T2:???神奇建图模型
【题目描述】
给定一颗N个点的有根树,其中1是树根,除了1以外的其他点u有唯一的父亲Fatheru。同时,给定M条路径,第i条路径是(si,ti),保证si是ti的祖先。你需要给每条路径染上红色或者蓝色。对于第i条路径,如果把它染成红色,需要花费代价pathRi;否则染成蓝色,需要花费代价pathBi。对于每个不是1的点u,考虑其连接它的父亲Fatheru的边,需要满足如下两个条件:
(1)覆盖了这条边,同时被染成红色的路径不超过Ru条;
(2)覆盖了这条边,同时被染成蓝色的路径不超过Bu条。
同时,对于这条边,如果有x条覆盖了它且被染成红色的路径,需要花费代价x×edgeRu;如果有y条覆盖了它且被染成蓝色的路径,需要花费代价y×edgeBu。
请你求出一个合法的染色方案,同时最小化它的代价。如果无解,请输出−1。
【输入格式】第一行两个非负整数N和M,表示树的点数和路径条数。接下来N−1行,第i行五个个非负整数Fatheri+1,Ri+1,Bi+1,edgeRi+1和edgeBi+1描述每个不是根的点的父亲
对连接父亲这条边的限制,以及代价的系数。接下来M行,第i行四个非负整数si,ti,pathRi和pathBi描述第i条路径。
【输出格式】如果有解则输出一行一个非负整数,表示最小的代价;否则输出−1表示无解。
显然这种费用最小,流量有限制的应该考虑网络流(费用流),但是没有想到比较好的建图方式,会出现“串边”的问题,就是从一条路径的ti进入从另一条路径的sj流出,这显然是不合法的,
我们需要做一些调整,首先发现,可以先让所有的路径都染成一个颜色,然后再作调整。(qzh:分RB两颗树,会串边(R经过次数和B经过次数都不同),一棵树不会)
调整如下:每条路径上的点,有两种选择:1.全变成另一种颜色,2.保持原来颜色
两种情况都必须:1.ti->si两个点都要有流量流入(上界=下界=1)
2.出现路径交叉不合法(交叉路径同色:无影响,交叉路径不同色:如下图)
【已经先染蓝色,向里面添加红色路径】
发现串边的情况并不存在(简单依次证明,或者说明流量守恒)
所以保留1.树边,[?,?],费用为er-eb,单位流量表示选择染红色
2.ti->si,[0,1],费用为pathB-pathR,单位流量表示选择染蓝色
3.S->ti,si->T,[1,1],费用为0,表示必须选
有上下界最小费最大流【注意建完新图之后,直接跑费用流就行,不用先求可行流,本质上是一样的】
code:
1 #include<bits/stdc++.h> 2 #define rep(i,l,r) for(int i=(l);i<=(r);i++) 3 #define LL long long 4 using namespace std; 5 const int maxn=200; 6 const LL inf=1e9; 7 int cur[maxn+10],fst[maxn+10],nxt[100000],w[100000],to[100000],c[100000],edge_count; 8 inline void add(int x,int y,int flow,int cost){ 9 edge_count++; 10 to[edge_count]=y; 11 w[edge_count]=flow; 12 c[edge_count]=cost; 13 nxt[edge_count]=fst[x]; 14 fst[x]=edge_count; 15 } 16 int d[maxn+10];//d[i]=in-out 17 int S,T,ss,tt; 18 inline void add(int x,int y,int down,int up,int cost){ 19 add(x,y,up-down,cost);add(y,x,0,-cost); 20 d[y]+=down;d[x]-=down; 21 } 22 int n,m; 23 LL dis[100000]; 24 queue<int >q; 25 bool vis[100000]; 26 LL maxflow,mincost; 27 inline bool SPFA(){ 28 while(q.size())q.pop(); 29 30 rep(i,1,tt)dis[i]=inf,vis[i]=0,cur[i]=fst[i]; 31 dis[ss]=0;vis[ss]=1; 32 q.push(ss); 33 while(q.size()){ 34 int u=q.front();q.pop(); 35 vis[u]=0; 36 for(int i=fst[u];i;i=nxt[i]){ 37 int v=to[i]; 38 if(w[i] && dis[v] > dis[u] + c[i]){ 39 dis[v]=dis[u]+c[i]; 40 if(!vis[v]){ 41 vis[v]=1; 42 q.push(v); 43 } 44 } 45 } 46 } 47 return dis[tt]<inf; 48 } 49 int dfs(int u,int flow){ 50 if(!flow || u==tt)return flow; 51 int fl=0,f;vis[u]=1;//must 52 for(int &i=cur[u];i;i=nxt[i]){ 53 int v=to[i]; 54 if( ! vis[v] && dis[v]==dis[u]+c[i] && ( f=dfs(v,min(flow,w[i] ) ) ) ){ 55 //^ must 56 w[i]-=f; 57 w[i^1]+=f; 58 fl+=f; 59 flow-=f; 60 mincost+=f*c[i]; 61 if(!flow)break; 62 } 63 } 64 vis[u]=0;//must 65 return fl; 66 } 67 inline void MCMF(){ 68 while(SPFA())maxflow+=dfs(ss,inf); 69 } 70 int fa[maxn],r[maxn],b[maxn],er[maxn],eb[maxn],cnt[maxn],s[maxn],t[maxn],pr[maxn],pb[maxn]; 71 inline void clear(){ 72 memset(fst,0,sizeof fst); 73 edge_count=1; 74 } 75 int main(){ 76 scanf("%d%d",&n,&m); 77 S=n+1;T=S+1;ss=T+1;tt=ss+1; 78 rep(i,2,n)scanf("%d%d%d%d%d",&fa[i],&r[i],&b[i],&er[i],&eb[i]); 79 clear(); 80 rep(i,1,m){ 81 scanf("%d%d%d%d",&s[i],&t[i],&pr[i],&pb[i]); 82 add(S,t[i],1,1,0); 83 add(s[i],T,1,1,0); 84 add(t[i],s[i],0,1,pb[i]-pr[i]); 85 mincost+=pr[i]; 86 while(t[i]!=s[i]){ 87 cnt[t[i]]++; 88 mincost+=eb[t[i]]; 89 t[i]=fa[t[i]]; 90 } 91 } 92 rep(i,2,n){ 93 if(r[i]+b[i]<cnt[i]){ 94 printf("-1"); 95 return 0; 96 } 97 add(i,fa[i],cnt[i]-b[i],r[i],er[i]-eb[i]); 98 mincost+=(cnt[i]-b[i])*(er[i]-eb[i]); 99 } 100 add(T,S,0,inf,0); 101 rep(i,1,T){ 102 if(d[i]>0)add(ss,i,0,d[i],0); 103 else add(i,tt,0,-d[i],0); 104 } 105 MCMF(); 106 printf("%lld",mincost); 107 return 0; 108 }
(注意加反向边)
LCT:非常重要的数据结构(都挺重要,难写难调)
容易T掉,注意常数,本蒟蒻就没注意。
1 #include<bits/stdc++.h> 2 #define LL long long 3 using namespace std; 4 template<typename T> 5 inline void read(T &a){ 6 a=0;bool b=0;char x=getchar(); 7 while(x<'0'||'9'<x){ 8 if(x=='-')b=1; 9 x=getchar(); 10 } 11 while('0'<=x&&x<='9'){ 12 a=(a<<1)+(a<<3)+x-'0'; 13 x=getchar(); 14 } 15 if(b)a=-a; 16 } 17 char C_[50]; 18 int TEMP; 19 template<typename T> 20 inline void write(T a){ 21 if(a<0){ 22 putchar('-'); 23 a=-a; 24 } 25 do{ 26 C_[++TEMP]=a%10+'0'; 27 a/=10; 28 }while(a); 29 while(TEMP)putchar(C_[TEMP--]); 30 } 31 const int maxn=2e5; 32 struct Node{ 33 int fa,ch[2],val,xsum; 34 bool rev; 35 Node(){ 36 fa=ch[0]=ch[1]=val=xsum=rev=0; 37 } 38 }t[maxn]; 39 int n,m; 40 inline void maintain(int x){ 41 t[x].xsum=t[x].val^t[t[x].ch[1]].xsum^t[t[x].ch[0]].xsum; 42 } 43 inline void pushrev(int x){ 44 swap(t[x].ch[1],t[x].ch[0]); 45 t[x].rev^=1; 46 } 47 inline void pushdown(int x){ 48 if(t[x].rev){ 49 if(t[x].ch[0])pushrev(t[x].ch[0]); 50 if(t[x].ch[1])pushrev(t[x].ch[1]); 51 t[x].rev=0; 52 } 53 } 54 inline int get(int x){ 55 if(x!=t[t[x].fa].ch[1]&&x!=t[t[x].fa].ch[0])return -1; 56 else return x==t[t[x].fa].ch[1]; 57 } 58 inline void rotate(int x){ 59 int f=t[x].fa; 60 bool b=get(x); 61 if(get(f)!=-1)t[t[f].fa].ch[get(f)]=x;//不用担心虚边会错连成实边 62 t[x].fa=t[f].fa; 63 t[f].fa=x; 64 t[f].ch[b]=t[x].ch[!b]; 65 if(t[f].ch[b])t[t[f].ch[b]].fa=f; 66 t[x].ch[!b]=f; 67 maintain(f);//多更新好多次 68 } 69 inline void print(int x){ 70 if(t[x].ch[0])print(t[x].ch[0]); 71 printf(" %d %d:%d",t[x].fa,x,t[x].rev); 72 if(t[x].ch[1])print(t[x].ch[1]); 73 } 74 inline void splay(int x){ 75 // if(get(x)==-1)return;//注意!!!不能直接返回,否则标记无法放掉,出现鬼畜错误 76 stack<int >sta; 77 //标记一定???从上往下放:否则会出现下面标记放掉,上面标记任在,放不完全的问题 78 //防止找根时出错??? 79 int f=x; 80 sta.push(x); 81 while(get(f)!=-1){ 82 sta.push(t[f].fa); 83 f=t[f].fa; 84 } 85 while(sta.size()){ 86 pushdown(sta.top()); 87 sta.pop(); 88 } 89 while(get(x)!=-1){ 90 f=t[x].fa; 91 if(get(f)!=-1 && get(f)==get(x))rotate(f); 92 else rotate(x); 93 } 94 maintain(x); 95 } 96 inline void access(int x){ 97 for(int y=0;x;y=x,x=t[x].fa){//注意y=0,保证第一次断开实边 98 splay(x); 99 t[x].ch[1]=y; 100 maintain(x);//儿子有变,更新 101 } 102 } 103 inline void makeroot(int x){ 104 access(x); 105 splay(x); 106 pushrev(x); 107 } 108 inline int findroot(int x){ 109 access(x); 110 //if(x==3)for(int i=1;i<=n;i++)print(i),putchar('\n'); 111 splay(x); 112 //if(x==3)for(int i=1;i<=n;i++)print(i),putchar('\n'); 113 while(t[x].ch[0]){ 114 pushdown(x);//??? 115 x=t[x].ch[0]; 116 } 117 splay(x); 118 //if(x==3)for(int i=1;i<=n;i++)print(i),putchar('\n'); 119 return x; 120 } 121 inline bool link(int x,int y){ 122 makeroot(x); 123 //for(int i=1;i<=n;i++)print(i),putchar('\n'); 124 if(x==findroot(y))return 0; 125 t[x].fa=y; 126 return 1; 127 } 128 inline bool cut(int x,int y){ 129 makeroot(x); 130 if(findroot(y)!=x || t[y].fa!=x || t[y].ch[0])return 0; 131 t[y].fa=t[x].ch[1]=0; 132 maintain(x);//没有儿子,更新 133 return 1; 134 } 135 inline void split(int x,int y){ 136 makeroot(x); 137 access(y); 138 splay(y); 139 //print(y);putchar('\n'); 140 } 141 int main(){ 142 scanf("%d%d",&n,&m); 143 for(int i=1;i<=n;i++){ 144 scanf("%d",&t[i].val); 145 t[i].xsum=t[i].val; 146 } 147 for(int i=1,opt,x,y;i<=m;i++){ 148 scanf("%d%d%d",&opt,&x,&y); 149 if(!opt){ 150 split(x,y); 151 printf("%d\n",t[y].xsum); 152 } 153 else if(opt==1){ 154 link(x,y); 155 } 156 else if(opt==2){ 157 cut(x,y); 158 } 159 else { 160 splay(x); 161 t[x].val=y; 162 maintain(x); 163 } 164 // for(int i=1;i<=n;i++)print(i),putchar('\n'); 165 } 166 return 0; 167 }
好啦好啦,我又回来啦。
那么复习数据结构吧,刚刚看了看单调栈这个东西,发现并没有想象中那么简单,这东西本质上是维护一个单调的序列,但是并不只是能求出该位置前后第一个> or < 的位置,还是可以利用单调性做些事情的
BZOJ2086
自己也是退役很久了,都不太会做题了,T1就不会了。
O(n)求出比一个数大的最后位置(比一个数小的最前位置),我们发现,如果一个序列是单调的,那么他只有第一个位置有意义,所以我们利用这个性质,用一个位置代替后面所有不如他优的位置,也就是单调栈,然后再进行比较,可以得到答案。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define rep(i,l,r) for(int i=l;i<=r;i++) 4 #define irp(i,r,l) for(int i=r;i>=l;i--) 5 using namespace std; 6 template<typename T> 7 inline void read(T &x){ 8 char ch;x=0;ch=getchar();bool b=0; 9 while(ch<'0' || '9'<ch){ 10 if(ch=='-')b=1; 11 ch=getchar(); 12 } 13 while('0'<=ch && ch<='9'){ 14 x=(x<<1)+(x<<3)+ch-'0'; 15 ch=getchar(); 16 } 17 if(b)x=-x; 18 } 19 const int maxn=1e6+5; 20 LL sum[maxn]; 21 int n,m,a[maxn],k; 22 int sta[maxn],p; 23 int main(){ 24 read(n);read(m); 25 rep(i,1,n)read(a[i]); 26 while(m--){ 27 p=0; 28 int ans=0; 29 read(k); 30 rep(j,1,n){ 31 sum[j]=sum[j-1]+a[j]-k; 32 if(sum[j]<sum[sta[p]])sta[++p]=j; 33 } 34 irp(j,n,1){ 35 while(sum[sta[p-1]]<=sum[j] && p)p--; 36 ans=max(ans,j-sta[p]); 37 } 38 printf("%d ",ans); 39 } 40 return 0; 41 }
单调队列的理解:同单调栈一样,并不只局限于维护定长序列的最大or最小值。而是在维护单调性的同时满足一些其他的性质,在不满足的时候,让左端电来回移动。然后右端点在添加的时候维护这个性质,从而得到我们想要的东西。
BZOJ2276
这个题,让我们在一堆不等式里选一段连续的,并且可以得到不降解。
那么我们发现,一个新增加的上届,仅仅对之前的解有影响,也就是说,之前的下界都得<=当前上界,所以这个东西就成为我们维护左端点的依据,然后,我们所要求的区间,下届的只要<=当前上界就可以了。那么我们维护单调减队列。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e6+5; 4 int n,l[maxn],r[maxn],ans; 5 int q[maxn],front,rear,lft=1; 6 bool ban[maxn]; 7 int main(){ 8 scanf("%d",&n); 9 for(int i=1;i<=n;i++)scanf("%d%d",&l[i],&r[i]); 10 11 q[front=1]=1; 12 for(int i=1;i<=n;i++){//右端点 逼迫 左端点(允许温度的最小值)的最大值 13 14 if(front<=rear){ 15 while(front<=rear && l[ q[rear] ]<=l[i]){ 16 rear--; 17 } 18 } 19 q[++rear]=i; 20 21 while(front<=rear && l[ q[front] ]>r[i]){ 22 lft++; 23 if(lft>q[front])front++; 24 } 25 ans=max(ans,i-lft+1); 26 } 27 printf("%d",ans); 28 return 0; 29 }