0127板子题

0127板子题

1、滑动窗口

有一个长为 n 的序列 a,以及一个大小为 k 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。

审题:单调队列模版啊

代码如下

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 char buf[1<<21],*p1=buf,*p2=buf;
 4 inline int getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
 5 template <typename T>
 6 inline void read(T &x){
 7    x=0;bool f=0;char ch=getc();
 8    while(!isdigit(ch)){if(ch=='-'){f = 1;}ch=getc();}
 9    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getc();}
10    x=f?-x:x;
11    return ;
12 }
13 const int N=1e6+10;
14 int a[N],n,k,f,i,j,Min[N],Max[N];
15 deque<int> x,y;
16 int main(){
17 //    freopen("1.in","r",stdin);
18 //    freopen("1.out","w",stdout);
19     read(n);read(k);
20     for(int i=1;i<=n;i++)read(a[i]);
21     x.push_front(1); y.push_front(1);
22     Max[1]=1; Min[1]=1;
23     k--;
24     for(int i=2;i<=n;i++){
25         while (x.size()&&x.front()+k<i){
26             x.pop_front();
27         }
28         while (y.size()&&y.front()+k<i){
29             y.pop_front();
30         }
31 
32         while (x.size()&&a[x.back()]>=a[i]){
33             x.pop_back();
34         }
35         while (y.size()&&a[y.back()]<=a[i]){
36             y.pop_back();
37         }
38         x.push_back(i); y.push_back(i);
39 
40         Min[i]=x.front(); Max[i]=y.front();
41     }
42     for(int i=k+1;i<=n;i++) cout<<a[Min[i]]<<" ";
43     cout<<endl;
44     for(int i=k+1;i<=n;i++) cout<<a[Max[i]]<<" ";
45     return 0;
46 }

2、最短路

给一个n(1<=n<=2500)个点m(1<=m<=6200)条边的无向图,求s到t的最短路。

由于出题人的良心,卡了我Floyd,嗯,所以只有80

为什么我当时会选择Floyd呢?因为我现在只记得这个算法了。。。但是由于这个可怕的时间复杂度,所以只能选择关于这道题相对快一点点的Dijkstra堆优化。

我忘了,于是我就看看我曾经写的总结。。。(就是这个啊

我写了个啥???所以我要详细写一遍。。。

因为解决的是单源最短路,所以f数组是装的是从s到各个点的最短距离,mapp装的是边的长度,vis是标记这个点去过没有

每一次算最短路时,先找最短的没有被标记过的边,将此边标记,然后用这个最小值更新所有边。

这就是dij函数里的基本内容。

下面是未优化的朴素Dijkstra

#include<bits/stdc++.h>
using namespace std;
int n,m,mapp[505][505],f[100009];
bool vis[505];
long long dij(){
    memset(f,0x3f,sizeof(f));
    f[1]=0;
    for(int j=1;j<=n;j++){
        int t=-1;
        for(int i=1;i<=n;i++){
            if(vis[i]==false){
                if(t==-1) t=i;
                else if(f[i]<f[t]) t=i;
            }
        }
        vis[t]=true;
        for(int i=1;i<=n;i++){
            f[i]=min(f[i],f[t]+mapp[t][i]);
        }
    }
    if(f[n]==0x3f3f3f3f) return -1;
    return f[n];
}
int main(){
    cin>>n>>m;
    memset(mapp,0x3f,sizeof(mapp));
    for(int i=1;i<=m;i++){
        int be,en,wei;
        cin>>be>>en>>wei;
        mapp[be][en]=min(mapp[be][en],wei);
    }
    for(int i=1;i<=n;i++){
        mapp[i][i]=0;
    }
    int t=dij();
    cout<<t;
    return 0;
}

 

下面是堆优化

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=1e6+10,M=2e6+10;
 4 struct node{
 5     int to,next,val;
 6 }edge[M];
 7 typedef pair<int,int> PII;
 8 priority_queue<PII,vector<PII>,greater<PII> > q;
 9 int h[N],idx=0,dist[N],n,m;
10 bool st[N];
11 void add(int x,int y,int z){
12     edge[idx].to=y;edge[idx].val=z;
13     edge[idx].next=h[x];h[x]=idx++;
14 }
15 void dijkstra(){
16     memset(dist,0x3f,sizeof(dist));
17     dist[1]=0;
18     q.push({0,1});
19     while(!q.empty()){
20         PII t=q.top();
21         q.pop();
22         int dis=t.first,u=t.second;
23         if(st[u]) continue;
24         st[u]=true;
25         for(int i=h[u];i!=-1;i=edge[i].next){
26             int j=edge[i].to;
27             if(dist[j]>edge[i].val+dis){
28                 dist[j]=edge[i].val+dis;
29                 q.push({dist[j],j});
30             }
31         }
32     }
33 }
34 int main(){
35     cin>>n>>m;
36     memset(h,-1,sizeof(h));
37     for(int i=1;i<=m;i++){
38         int u,v,w;
39         cin>>u>>v>>w;
40         add(u,v,w);
41     }
42     dijkstra();
43     if(dist[n]>0x3f3f3f3f/2) cout<<-1;
44     else cout<<dist[n];
45     return 0;
46 }

 3、SPFA判负

给定一个n个点m条边的有向图,图中可能存在重边和自环,边权可能为负数。请你判断图中是否存在负权回路。

SPFA是Bellman-Ford算法的优化版,避免了后者“用一个还没有被松弛的点去松弛另外的点去松弛另外的点”的情况,就使用队列来储存已被松弛的点,再用队列中点点去松弛其他的点

所以如何判负环呢???

有两个版本,bfs和dfs。

bfs:用cnt[i]表示起点s到i的距离(最短距离)包含的点的个数,初始化cnt[s]=1;松弛的时候,同时更新cnt[v]=cnt[u]+1。若此时cnt[v]>n,则存在负环。或记录每个点的入队次数,大于n则有负环。但此方法较前者更慢。

dfs:将那个队列(先进先出)换成栈(先进后出),每次都以刚松弛的点来松弛其他点,如果能够松弛点x,并x在栈中,那么图中就有负环。

比较:若存在负环,dfs比bfs快,若无负环,bfs比dfs快

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int SIZE=10009;
 4 int n,m,tot;
 5 int nex[SIZE],head[SIZE],v[SIZE],w[SIZE];
 6 bool vis[SIZE];
 7 int d[SIZE],cnt[SIZE];
 8 
 9 void add(int x,int y,int z){ 
10     tot++;
11     nex[tot]=head[x];
12     head[x]=tot;
13     v[tot]=y;w[tot]=z;
14 }
15 
16 int SPFA(int s){
17     int x,y,i,j;
18     queue<int>q;
19     memset(d,10009,sizeof(d));//初始化 
20     memset(vis,false,sizeof(vis));//初始化 
21     while(!q.empty())    q.pop();//初始化 
22     d[s]=0;
23     cnt[s]=1;
24     q.push(s);vis[s]=true;
25     while(!q.empty()){
26         x=q.front();
27         q.pop();
28         vis[x]=false;
29         for(int i=head[x];i;i=nex[i]){
30             y=v[i];
31             if(d[y]>d[x]+w[i]){//如果可以松弛 
32                 d[y]=d[x]+w[i];
33                 cnt[y]=cnt[x]+1;
34                 if(cnt[y]>n) return false;
35                 if(!vis[y]){
36                     q.push(y);
37                     vis[y]=true;
38                 }
39             }
40         }
41     }
42     return true;
43 }
44 
45 int main(){
46     int x,y,z,i;
47     cin>>n>>m;
48     for(int i=1;i<=m;i++){
49         cin>>x>>y>>z;
50         add(x,y,z);
51         add(y,x,z);
52     }
53     int flag=SPFA(1);
54     if(!flag)cout<<"Yes";
55     else cout<<"No";
56     return 0;
57 }

 

4、最小生成树

给定结点数为n,边数为m的带权无向连通图G,所有结点编号为1,2,3...n。求G的最小生成树的边权和。

版子题,Kruskal:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int read(){
 4     int x=1,a=0;
 5     char ch=getchar();
 6     while(ch>'9'||ch<'0'){
 7         if(ch=='-') x=-1;
 8         ch=getchar();
 9     }
10     while(ch<='9'&&ch>='0'){
11         a=(a<<1)+(a<<3)+ch-48;
12         ch=getchar();
13     }
14     return x*a;
15 }
16 int n,m;
17 long long sum;
18 int fa[600009];
19 int cnt;
20 struct Edge{
21     int a,b,w;
22 }node[600009];
23 bool cmp(Edge a,Edge b){
24     return a.w<b.w;
25 }
26 int get(int x){//并查集查询子节点点 
27     if(x==fa[x]) return x;
28     return fa[x]=get(fa[x]);
29 }
30 int merge(int x,int y){//并查集合并 
31     fa[get(x)]=get(y);
32 }
33 void Kruskal(){
34     sort(node+1,node+m+1,cmp);
35     for(int i=1;i<=n;i++) fa[i]=i;
36     for(int i=1;i<=m;i++){
37         int u=node[i].a,v=node[i].b,w=node[i].w;
38         if(get(u)!=get(v)){
39             merge(u,v);
40             cnt++;
41             sum+=w;
42         }
43     }
44 } 
45 int main(){
46 //    freopen("4.in","r",stdin);
47 //    freopen("4.out","w",stdout);
48     n=read();m=read();
49     for(int i=1;i<=m;i++){
50         int u=read(),v=read(),w=read();
51         node[i].a=u;node[i].b=v;
52         node[i].w=w;
53     }
54     Kruskal();
55     cout<<sum;
56     return 0;
57 }

 

5、并查集

这是一道模板题。 维护一个n点的无向图,支持:

加入一条连接u和v的无向边

查询u和v的连通性

注意:由于本题数据较大,因此输出的时候采用特殊的输出方式:用0或1代表每个询问的答案,将每个询问的答案依次从左到右排列,把得到的串视为一个二进制数,输出这个二进制数 mod 998244353的值。

请务必使用快读。

审题:“注意”中,在一个二进制的数的最后一位加上1,则对应的十进制的数就是乘以二加上一,在一个二进制的数的最后一位加上0,则对应的十进制的数就是乘以二。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 char buf[1<<21],*p1=buf,*p2=buf;
 4 inline int getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
 5 template <typename T>
 6 inline void read(T &x){
 7    x=0;bool f=0;char ch=getc();
 8    while(!isdigit(ch)){if(ch=='-'){f = 1;}ch=getc();}
 9    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getc();}
10    x=f?-x:x;
11    return ;
12 }
13 int n,m;
14 long long sum;
15 int fa[4000009];
16 int get(int x){//并查集查询子节点点 
17     if(x==fa[x]) return x;
18     return fa[x]=get(fa[x]);
19 }
20 int merge(int x,int y){//并查集合并 
21     fa[get(x)]=get(y);
22 }
23 int main(){
24 //    freopen("5.in","r",stdin);
25 //    freopen("5.out","w",stdout);
26     read(n);read(m);
27     for(int i=0;i<=n;i++){
28         fa[i]=i;
29     }
30     for(int i=1;i<=m;i++){
31         int op,u,v;
32         read(op);read(u);read(v);
33         if(op==0) merge(u,v);
34         else{
35             if(get(u)==get(v)) sum=sum*2+1;
36             else sum=sum*2;
37             sum%=998244353;
38         }
39     }
40     cout<<sum;
41     return 0;
42 }

6、树状数组I-单点修改,区间查询

如题,已知一个数列,你需要进行下面两种操作:

将某一个数加上x

求出某区间每一个数的和

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 template <typename T>
 4 inline void read(T &x){
 5     x=0;char ch=getchar();bool f=false;
 6     while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
 7     while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
 8     x=f?-x:x;
 9     return ;
10 }
11 template <typename T>
12 inline void write(T x){
13     if(x<0) putchar('-'),x=-x;
14     if(x>9) write(x/10);
15     putchar(x%10^48);
16     return ;
17 }
18 int n,m;
19 long long c[1000009];
20 long long get(int x){
21     long long sum=0;
22     for(;x;x-=x&-x) sum+=c[x];
23     return sum;
24 }
25 void add(int x,int y){
26     for(;x<=n;x+=x&-x) c[x]+=y;
27 }
28 int main(){
29 //    freopen("6.in","r",stdin);
30 //    freopen("6.out","w",stdout);
31     read(n);read(m);
32 //    cin>>n>>m;
33     for(int i=1;i<=n;i++){
34         int kkk;
35         read(kkk);
36         add(i,kkk);
37     }
38     for(int i=1;i<=m;i++){
39         int op,num,d;
40         read(op);read(num);read(d);
41         if(op==1) add(num,d);
42         else{
43             long long ans=0;
44             ans=get(d)-get(num-1);
45             cout<<ans<<endl;
46         }
47     }
48     return 0;
49 }

 

7、树状数组II-区间修改,单点查询

 

如题,已知一个数列,你需要进行下面两种操作:

1. 将某区间每一个数数加上x;

2. 求出某一个数的值。

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 char buf[1<<21],*p1=buf,*p2=buf;
 4 inline int getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
 5 template <typename T>
 6 inline void read(T &x){
 7    x=0;bool f=0;char ch=getc();
 8    while(!isdigit(ch)){if(ch=='-'){f = 1;}ch=getc();}
 9    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getc();}
10    x=f?-x:x;
11    return ;
12 }
13 const int SIZE=1000010;
14 struct SegmentTree{
15     int l,r;
16     long long sum,flag;
17     #define l(x) tree[x].l
18     #define r(x) tree[x].r
19     #define sum(x) tree[x].sum
20     #define flag(x) tree[x].flag
21 }tree[SIZE*4];
22 int a[SIZE],n,m;
23 void build(int p,int l,int r){
24     l(p)=l;r(p)=r;
25     if(l==r){sum(p)=a[l];return;}
26     int mid=(l+r)/2;
27     build(p*2,l,mid);
28     build(p*2+1,mid+1,r);
29     sum(p)=sum(p*2)+sum(p*2+1);
30 }
31 void spread(int p){
32     if(flag(p)){
33         sum(p*2)+=flag(p)*(r(p*2)-l(p*2)+1);
34         sum(p*2+1)+=flag(p)*(r(p*2+1)-l(p*2+1)+1);
35         flag(p*2)+=flag(p);
36         flag(p*2+1)+=flag(p);
37         flag(p)=0;
38     }
39 }
40 void change(int p,int l,int r,int d){
41     if(l<=l(p)&&r(p)<=r){
42         sum(p)+=(long long)d*(r(p)-l(p)+1);
43         flag(p)+=d;
44         return;
45     }
46     spread(p);
47     int mid=(l(p)+r(p))/2;
48     if(l<=mid) change(p*2,l,r,d);
49     if(r>mid) change(p*2+1,l,r,d);
50     sum(p)=sum(p*2)+sum(p*2+1); 
51 }
52 long long ask(int p,int l,int r){
53     if(l<=l(p)&&r>=r(p)) return sum(p);
54     spread(p);
55     int mid=(l(p)+r(p))/2;
56     long long val=0;
57     if(l<=mid) val+=ask(p*2,l,r);
58     if(r>mid) val+=ask(p*2+1,l,r);
59     return val;
60 }
61 int main(){
62 //    freopen("7.in","r",stdin);
63 //    freopen("7.out","w",stdout);
64     read(n);read(m);
65     for(int i=1;i<=n;i++) read(a[i]);
66     build(1,1,n);
67     while(m--){
68         int op,l,r,d;
69         read(op);read(l);
70         if(op==1){
71             read(r);read(d);
72             change(1,l,r,d);
73         }
74         else{
75             cout<<ask(1,l,l)<<endl;
76         }
77     }
78     return 0;
79 }

 


剩下的普通平衡树和高精除高精我已经不想打了,就这样吧

 

posted @ 2021-01-28 10:50  001A  阅读(110)  评论(0编辑  收藏  举报