回归——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(无删减版)

例题的话不太多,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 }
Code

 

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 }
AC Code

没有当前弧优化。找不到别的板子了。

 

费用流(原版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 }
AC Code

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 }
AC Code

T2:???神奇建图模型

【题目描述】

给定一颗N个点的有根树,其中1是树根,除了1以外的其他点u有唯一的父亲Fatheru同时,给定M条路径,第i条路径是(si,ti),保证siti的祖先。你需要给每条路径染上红色或者蓝色对于第i条路径,如果把它染成红色,需要花费代价pathRi;否则染成蓝色,需要花费代价pathBi对于每个不是1的点u考虑其连接它的父亲Fatheru的边,需要满足如下两个条件:

1覆盖了这条边,同时被染成红色的路径不超过Ru条;

2覆盖了这边,同时被染成蓝色的路径不超过Bu

同时,对于这条边,如果有x条覆盖了它且被染成红色的路径,需要花费代x×edgeRu;如果有y条覆盖了它且被染成蓝色的路径,需要花费代价y×edgeBu

请你求出一个合法的染色方案,同时最小化它的代价如果无解,请输出1

【输入格式第一行两个非负整NM,表示树的点数和路径条数。接下来N1行,第i五个非负整数Fatheri+1,Ri+1,Bi+1,edgeRi+1edgeBi+1描述每个不是根的点的父亲

连接父亲这条边的限制,以及代价的系接下来M行,第i四个非负整数si,ti,pathRipathBi描述第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 }
AC Code

(注意加反向边)

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 }
View Code

 好啦好啦,我又回来啦。

那么复习数据结构吧,刚刚看了看单调栈这个东西,发现并没有想象中那么简单,这东西本质上是维护一个单调的序列,但是并不只是能求出该位置前后第一个> 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 }
Ac code

单调队列的理解:同单调栈一样,并不只局限于维护定长序列的最大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 }
Ac Code

 

posted @ 2020-01-16 21:00  Tj1  阅读(246)  评论(0编辑  收藏  举报