0127板子题
0127板子题
有一个长为 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 }
给一个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 }
如题,已知一个数列,你需要进行下面两种操作:
将某一个数加上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 }
如题,已知一个数列,你需要进行下面两种操作:
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 }
剩下的普通平衡树和高精除高精我已经不想打了,就这样吧