考场做法用双指针向两侧更新,当左段点左移一位时,如果右端点不满足条件,则跳回肯定满足的位置。复杂度玄学
题解做法是类似最长子段和,如果有一个区间和为负,则维护的指针跳过去即可
考虑 dp
设 \(f[i][j][0/1][0/1]\) 表示长度为 \(i\) 的区间合并 \(j\) 次合并完,区间左边有没有值,右边有没有值的方案数
设这个区间的最大值位置在 \(k\),那么左边有 \(k-1\) 个值,右边 \(i-k\) 个值,考虑左右端点和合并次数结合组合意义转移即可
#include<bits/stdc++.h> using namespace std; const int maxn=1005; int c[maxn][maxn]; int n,m; long long f[maxn][maxn][2][2],mod; int read(){ int x=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-')f=-1; ch=getchar(); } while(isdigit(ch)){ x=x*10+ch-48; ch=getchar(); } return x*f; } void pre(){ c[0][0]=1; for(int i=1;i<=n;i++){ c[i][0]=1; for(int j=1;j<=i;j++){ c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; // cout<<c[i][j]<<" "; } } return ; } signed main(){ n=read(); m=read(); mod=read(); pre(); // cout<<c[5][2]<<endl; for(int i=0;i<=m;i++)f[0][i][0][0]=f[0][i][1][1]=f[0][i][0][1]=f[0][i][1][0]=1; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ for(int k=1;k<=i;k++){ f[i][j][0][0]=(f[i][j][0][0]+f[k-1][j][0][1]*f[i-k][j][1][0]%mod*c[i-1][k-1]%mod)%mod; f[i][j][0][1]=(f[i][j][0][1]+f[k-1][j][0][1]*f[i-k][j-1][1][1]%mod*c[i-1][k-1]%mod)%mod; f[i][j][1][0]=(f[i][j][1][0]+f[k-1][j-1][1][1]*f[i-k][j][1][0]%mod*c[i-1][k-1]%mod)%mod; f[i][j][1][1]=(f[i][j][1][1]+(f[k-1][j][1][1]*f[i-k][j][1][1]-(f[k-1][j][1][1]-f[k-1][j-1][1][1])*(f[i-k][j][1][1]-f[i-k][j-1][1][1]))%mod*c[i-1][k-1])%mod; if(f[i][j][1][1]<mod)f[i][j][1][1]=f[i][j][1][1]%mod+mod; // cout<<f[i][j][1][1]<<endl; } } } cout<<((f[n][m][0][0]-f[n][m-1][0][0])%mod+mod)%mod; return 0; }
对于回去的路一定是走一段到1的路径,再往回走一段来时的路径
将回去的路起点和终点用 \(a\)、\(b\) 表示,\(f[i][j]\) 表示起点为 \(b\),终点为 \(a\),\(g[i][j]\) 表示起点终点均为 \(a\) 点距离
转移跑最短路实现
#include<bits/stdc++.h> using namespace std; const int maxn=5e6+5; const int maxm=5e7+5; const int maxk=1005; const int inf=0x3f3f3f3f; int n,m,val[maxk],dis1[maxk][maxk],dis[maxn],hd[maxn],cnt,x,y; bool vis[maxn]; int read(){ int x=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-')f=-1; ch=getchar(); } while(isdigit(ch)){ x=x*10+ch-48; ch=getchar(); } return x*f; } int id(int x,int y,int z){ return z*n*n+(x-1)*n+y; } struct Edge{ int nxt,to,val; }edge[maxm]; void add(int u,int v,int w){ // cout<<u<<" "<<v<<" "<<w<<endl; edge[++cnt].nxt=hd[u]; edge[cnt].to=v; edge[cnt].val=w; hd[u]=cnt; return ; } void spfa(){ memset(dis,0x3f,sizeof dis); dis[id(n,n,0)]=0; queue<int>q; q.push(id(n,n,0)); while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=false; for(int i=hd[u];i;i=edge[i].nxt){ int v=edge[i].to; if(dis[v]>dis[u]+edge[i].val){ dis[v]=dis[u]+edge[i].val; if(!vis[v])vis[v]=true,q.push(v); } } } return ; } int main(){ n=read(); m=read(); for(int i=1;i<=n;i++)val[i]=read(); memset(dis1,0x3f,sizeof dis1); for(int i=1;i<=m;i++){ x=read(); y=read(); dis1[x][y]=val[y]; } for(int i=1;i<=n;i++)dis1[i][i]=0; for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis1[i][j]=min(dis1[i][j],dis1[i][k]+dis1[k][j]); // cout<<dis1[2][4]<<endl; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ for(int k=1;k<=n;k++){ if(dis1[j][k]!=inf&&j!=k){ add(id(i,j,0),id(k,i,1),dis1[j][k]-val[k]); } if(dis1[i][k]!=inf&&dis1[k][j]!=inf){ add(id(i,j,1),id(i,k,0),dis1[i][k]+dis1[k][j]); } } } } // cout<<"hhh"<<endl; spfa(); if(dis[id(1,1,0)]==inf)puts("-1"); else cout<<dis[id(1,1,0)]+val[1]; return 0; }
考虑扫描线做法
当一个矩形的下边界加入时在线段树上区间覆盖
上边界去除标记
考虑如果是维护联通性,那么当一个位置有多个矩形覆盖,只需要和任意一个连边即可,所以线段树上每个点只存一种颜色,查询时递归到只有一种颜色的区间然后连边
修改时判断将要替换的颜色和当前颜色消失的时间大小,只保留时间靠后的颜色
代码略丑且长
#include<bits/stdc++.h> using namespace std; int read(){ int x=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-')f=-1; ch=getchar(); } while(isdigit(ch)){ x=x*10+ch-48; ch=getchar(); } return x*f; } const int maxn=2e5+5; int n; namespace p1{ int fa[maxn],ans; struct Node{ int x,y,xx,yy; }a[maxn],b,c; int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); } void merge(int x,int y){ x=find(x); y=find(y); if(x!=y)fa[x]=y; return ; } void start(){ for(int i=1;i<=n;i++){ a[i].x=read(); a[i].y=read(); a[i].xx=read(); a[i].yy=read(); } for(int i=1;i<=n;i++){ fa[i]=i; } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(i==j)continue; if(max(a[i].x,a[j].x)<=min(a[i].xx,a[j].xx)&&max(a[i].y,a[j].y)<=min(a[i].y,a[j].yy))merge(i,j); continue; b=a[i]; c=a[j]; if(b.x>c.x||b.x==c.x&&b.y>c.y)swap(b,c); if(c.x<=b.xx){ if(c.y>=b.y&&c.y<=b.yy||c.yy>=b.y&&c.yy<=b.yy)merge(i,j); } } } for(int i=1;i<=n;i++){ if(fa[i]==i)ans++; } cout<<ans; return ; } } namespace p2{ int fa[maxn],x,y,xx,yy,ed[maxn],tot,ans; struct Node{ int x,xx,y,op,id; Node(){} Node(int a,int b,int c,int d,int e):x(a),xx(b),y(c),op(d),id(e){} }a[maxn]; bool cmp(Node a,Node b){ return a.y==b.y?a.op>b.op:a.y<b.y; } int find(int x){ return fa[x]==x?fa[x]:fa[x]=find(fa[x]); } void merge(int x,int y){ x=find(x); y=find(y); if(x!=y)fa[x]=y; return ; } struct Seg{ int l,r,type,num; bool lazy,lazy1,flag; }t[maxn*4]; void build(int p,int l,int r){ t[p].l=l; t[p].r=r; if(l==r)return ; int mid=l+r>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); return ; } void update(int p){ if(t[p<<1].num>1||t[p<<1|1].num>1)t[p].num=2; else if(t[p<<1].type==t[p<<1|1].type&&t[p<<1].num==1&&t[p<<1|1].num==1)t[p].num=1,t[p].type=t[p<<1].type; else t[p].num=t[p<<1].num+t[p<<1|1].num,t[p].type=t[p<<1].type+t[p<<1|1].type; if((!t[p<<1].num)||(!t[p<<1|1].num))t[p].flag=true; else t[p].flag=false; t[p].flag|=t[p<<1].flag|t[p<<1|1].flag; return ; } void spread(int p){ t[p<<1].type=t[p<<1|1].type=t[p].type; t[p<<1].lazy1=t[p<<1|1].lazy1=0; t[p<<1].num=t[p<<1|1].num=t[p<<1].lazy=t[p<<1|1].lazy=1; t[p].lazy=0; return ; } void spread1(int p){ t[p<<1].num=t[p<<1|1].num=t[p<<1].type=t[p<<1|1].type=t[p<<1].lazy=t[p<<1|1].lazy=0; t[p<<1].lazy1=t[p<<1|1].lazy1=1; t[p].lazy1=0; return ; } void ask(int p,int l,int r,int id){ if(t[p].num==0)return; if(t[p].l>=l&&t[p].r<=r&&t[p].num==1){ // cout<<"hhh "<<id<<" "<<t[p].type<<endl; // cout<<t[p].type<<endl;; // cout<<id<<" "<<t[p].type<<endl; merge(id,t[p].type); return ; } if(t[p].lazy1)spread1(p); if(t[p].lazy)spread(p); int mid=t[p].l+t[p].r>>1; if(l<=mid)ask(p<<1,l,r,id); if(r>mid)ask(p<<1|1,l,r,id); return ; } void change(int p,int l,int r,int id){ // cout<<"hhh "<<t[p].l<<" "<<t[p].r<<" "<<t[p].num<<" "<<t[p].flag<<" "<<id<<endl; if(t[p].l>=l&&t[p].r<=r&&t[p].num<=1){//浼樺寲!!! // cout<<t[p].type<<" "<<ed[t[p].type]<<endl; if(ed[t[p].type]<ed[id]){ // cout<<"ppp "<<t[p].l<<" "<<t[p].r<<" "<<id<<endl; t[p].type=id; t[p].lazy=1; t[p].num=1; t[p].lazy1=0; return ; } if(!t[p].flag)return ; // if(t[p].l==t[p].r)return ; } if(t[p].lazy1)spread1(p); if(t[p].lazy)spread(p); int mid=t[p].l+t[p].r>>1; if(l<=mid)change(p<<1,l,r,id); if(r>mid)change(p<<1|1,l,r,id); update(p); return ; } void del(int p,int l,int r,int id){ // cout<<t[p].l<<" "<<t[p].r<<endl; if(t[p].l>=l&&t[p].r<=r&&t[p].num<=1){ if(t[p].type==id){ t[p].type=t[p].num=t[p].lazy=0; t[p].lazy1=1; } return ; } if(t[p].lazy1)spread1(p); if(t[p].lazy)spread(p); int mid=t[p].l+t[p].r>>1; if(l<=mid)del(p<<1,l,r,id); if(r>mid)del(p<<1|1,l,r,id); update(p); return ; } void start(){ int mx=0; for(int i=1;i<=n;i++){ x=read(); y=read(); xx=read(); mx=max(mx,xx); yy=read(); a[++tot]=Node(x,xx,y,1,i); a[++tot]=Node(x,xx,yy,-1,i); ed[i]=yy; } build(1,1,mx); for(int i=1;i<=n;i++)fa[i]=i; sort(a+1,a+tot+1,cmp); // cout<<endl; for(int i=1;i<=tot;i++){ // cout<<a[i].y<<" "<<a[i].x<<" "<<a[i].xx<<" "<<a[i].id<<" "<<a[i].op<<" "<<ed[a[i].id]<<endl; if(a[i].op==1){ ask(1,a[i].x,a[i].xx,a[i].id); change(1,a[i].x,a[i].xx,a[i].id); } else{ del(1,a[i].x,a[i].xx,a[i].id); } } for(int i=1;i<=n;i++){ if(fa[i]==i)ans++; } cout<<ans; } } int main(){ // freopen("shuju.in","r",stdin); // freopen("my.out","w",stdout); n=read(); p2::start(); return 0; }