CSP-S 模拟赛 34
rnk12,\(24+50+20+0=94\)。
T1 玩游戏
有一个痿正解:从 \(k\) 到 \(1\) 扫左端点,对于每个左端点扫它最远能到达的右端点。如果在任何一时刻它的右端点位置 \(<k\),则断定输出 No
。否则检查当左端点到 \(1\) 时右端点能否到 \(n\)。注意这里扫右端点的方式,不要每次都从 \(k\) 向右扫,而是把右端点定义在外面,然后用和莫队类似的那种方法扫,就极大地降低了复杂度。实测最终 TLE 91pts,很可以了。
考场上连这都想不到?该退役了。
真正的正解貌似更复杂?唐了。
#include<bits/stdc++.h>
#define fw fwrite(obuf,p3-obuf,1,stdout)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#define putchar(x) (p3-obuf<1<<20?(*p3++=(x)):(fw,p3=obuf,*p3++=(x)))
using namespace std;char buf[1<<20],obuf[1<<20],*p1=buf,*p2=buf,*p3=obuf,str[20<<2];
template<typename T=int>
T read(){T x=0,f=1;char ch=getchar();while(!isdigit(ch))ch=='-'&&(f=-1),ch=getchar();while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();return x*f;
}
template<typename T>
void write(T x,char sf='\n'){if(x<0)putchar('-'),x=~x+1;int top=0;do str[top++]=x%10,x/=10;while(x);while(top)putchar(str[--top]+48);if(sf^'#')putchar(sf);
}
void writes(string s){for(auto x:s)putchar(x);putchar('\n');
}
using ll=long long;
constexpr int MAXN=1e5+5;
int T,n,k;
ll a[MAXN],sum[MAXN];int main(){freopen("game.in","r",stdin);freopen("game.out","w",stdout);T=read();while(T--){n=read(),k=read();memset(sum,0,sizeof(ll)*(n+5));for(int i=1;i<=n;++i)a[i]=read<ll>();if(sum[n]-sum[1]>0){writes("No");continue;}for(int i=k-1;i;--i)sum[i]=sum[i+1]+a[i+1];for(int i=k+1;i<=n;++i)sum[i]=sum[i-1]+a[i];int r=k;for(int i=k;i;--i){while(sum[r+1]+sum[i]<=0&&r<n)++r;while(sum[r]+sum[i]>0&&r)--r;if(r<k){writes("No");goto byby;}}writes(r==n?"Yes":"No");byby:;}return fw,0;
}
T2 排列
不用笛卡尔树。设 \(f_{i,j,0/1,0/1}\) 表示长度为 \(i\) 的区间 \(j\) 次合并完,左右两边是否靠墙的前缀和。用前缀和优化 DP。
- 对于 00 的情况(两边都靠墙),可以由 \(j\) 相同的 01 和 10 转移。
- 对于 10 的情况,可以由 \(j-1\) 的 11 和 \(j\) 相同的 10 转移。
- 对于 01 的情况,同理。
- 对于 11 的情况,前缀和神秘转移。
神秘题没什么好说的。
#include<bits/stdc++.h>
#define fw fwrite(obuf,p3-obuf,1,stdout)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#define putchar(x) (p3-obuf<1<<20?(*p3++=(x)):(fw,p3=obuf,*p3++=(x)))
#define int long long
using namespace std;char buf[1<<20],obuf[1<<20],*p1=buf,*p2=buf,*p3=obuf,str[20<<2];
template<typename T=int>
T read(){T x=0,f=1;char ch=getchar();while(!isdigit(ch))ch=='-'&&(f=-1),ch=getchar();while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();return x*f;
}
template<typename T>
void write(T x,char sf='\n'){if(x<0)putchar('-'),x=~x+1;int top=0;do str[top++]=x%10,x/=10;while(x);while(top)putchar(str[--top]+48);if(sf^'#')putchar(sf);
}
constexpr int MAXN=1005;
int n,K,P;
int C[MAXN][MAXN];
int f[MAXN][MAXN][2][2];
void add(int&x,int y){x=x+y>=P?x+y-P:x+y;
}
int sub(int x,int y){return x-y<0?x-y+P:x-y;
}signed main(){freopen("per.in","r",stdin);freopen("per.out","w",stdout);n=read(),K=read(),P=read();for(int i=0;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])%P/*,write(C[i][j])*/;}for(int i=0;i<=K;++i)f[0][i][0][0]=f[0][i][0][1]=f[0][i][1][0]=f[0][i][1][1]=1;for(int i=1;i<=n;++i)for(int k=1;k<=K;++k)for(int j=1;j<=i;++j){add(f[i][k][0][0],f[j-1][k][0][1]*f[i-j][k][1][0]%P*C[i-1][j-1]%P);add(f[i][k][0][1],f[j-1][k][0][1]*f[i-j][k-1][1][1]%P*C[i-1][j-1]%P);add(f[i][k][1][0],f[j-1][k-1][1][1]*f[i-j][k][1][0]%P*C[i-1][j-1]%P);add(f[i][k][1][1],sub(f[j-1][k][1][1]*f[i-j][k][1][1]%P,sub(f[j-1][k][1][1],f[j-1][k-1][1][1])*sub(f[i-j][k][1][1],f[i-j][k-1][1][1])%P)*C[i-1][j-1]%P);}write(sub(f[n][K][0][0],f[n][K-1][0][0]));return fw,0;
}
T3 最短路
假做法:两遍 Dijkstra。
真假做法:对于每一条边建一条反边,跑二维 Dij。设 \(\mathit{dis}_{i,j}\) 表示沿正边到 \(i\) 点和反边到 \(j\) 的距离之和,则 \(\mathit{dis}_{n,n}\) 即为题中所求。用 bitset 维护原本的 \(\mathit{vis}\) 数组。
#include<bits/stdc++.h>
#define fw fwrite(obuf,p3-obuf,1,stdout)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#define putchar(x) (p3-obuf<1<<20?(*p3++=(x)):(fw,p3=obuf,*p3++=(x)))
using namespace std;char buf[1<<20],obuf[1<<20],*p1=buf,*p2=buf,*p3=obuf,str[20<<2];
template<typename T=int>
T read(){T x=0,f=1;char ch=getchar();while(!isdigit(ch))ch=='-'&&(f=-1),ch=getchar();while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();return x*f;
}
template<typename T>
void write(T x,char sf='\n'){if(x<0)putchar('-'),x=~x+1;int top=0;do str[top++]=x%10,x/=10;while(x);while(top)putchar(str[--top]+48);if(sf^'#')putchar(sf);
}
constexpr int MAXN=255;
int n,m,p[MAXN];
int hd1[MAXN],tot1,hd2[MAXN],tot2;
struct{int v,nxt;}e1[MAXN*MAXN],e2[MAXN*MAXN];
void addedge(int typ,int u,int v){if(typ==1){e1[++tot1]={v,hd1[u]};hd1[u]=tot1;}else{e2[++tot2]={v,hd2[u]};hd2[u]=tot2;}
}
struct Node{int s,x,y;Node(){}Node(int s,int x,int y):s(s),x(x),y(y){}bool operator<(const Node&b)const{return s>b.s;}
};
int dis[MAXN][MAXN],vis[MAXN][MAXN];
bitset<MAXN>bit[MAXN][MAXN];
void dijkstra(){memset(dis,0x3f,sizeof(dis));dis[1][1]=p[1];bit[1][1][1]=1;priority_queue<Node>q;q.emplace(dis[1][1],1,1);while(!q.empty()){int x=q.top().x,y=q.top().y;q.pop();if(vis[x][y])continue;vis[x][y]=1;for(int i=hd1[x],tmp;i;i=e1[i].nxt){tmp=0;if(!bit[x][y][e1[i].v])tmp=p[e1[i].v];if(dis[e1[i].v][y]>dis[x][y]+tmp){dis[e1[i].v][y]=dis[x][y]+tmp;bit[e1[i].v][y]=bit[x][y];bit[e1[i].v][y][e1[i].v]=1;q.emplace(dis[e1[i].v][y],e1[i].v,y);}}for(int i=hd2[y],tmp;i;i=e2[i].nxt){tmp=0;if(!bit[x][y][e2[i].v])tmp=p[e2[i].v];if(dis[x][e2[i].v]>dis[x][y]+tmp){dis[x][e2[i].v]=dis[x][y]+tmp;bit[x][e2[i].v]=bit[x][y];bit[x][e2[i].v][e2[i].v]=1;q.emplace(dis[x][e2[i].v],x,e2[i].v);}}}
}int main(){freopen("tour.in","r",stdin);freopen("tour.out","w",stdout);n=read(),m=read();for(int i=1;i<=n;++i)p[i]=read();for(int i=1,u,v;i<=m;++i){u=read(),v=read();addedge(1,u,v),addedge(2,v,u);}dijkstra();write(dis[n][n]==0x3f3f3f3f?-1:dis[n][n]);return fw,0;
}
T4 矩形
扫描线 + 并查集。讲得很清楚了。
#include<bits/stdc++.h>
#define fw fwrite(obuf,p3-obuf,1,stdout)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#define putchar(x) (p3-obuf<1<<20?(*p3++=(x)):(fw,p3=obuf,*p3++=(x)))
using namespace std;char buf[1<<20],obuf[1<<20],*p1=buf,*p2=buf,*p3=obuf,str[20<<2];
template<typename T=int>
T read(){T x=0,f=1;char ch=getchar();while(!isdigit(ch))ch=='-'&&(f=-1),ch=getchar();while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();return x*f;
}
template<typename T>
void write(T x,char sf='\n'){if(x<0)putchar('-'),x=~x+1;int top=0;do str[top++]=x%10,x/=10;while(x);while(top)putchar(str[--top]+48);if(sf^'#')putchar(sf);
}
constexpr int MAXN=1e5+5;
int n,f[MAXN];
int find(int x){if(f[x]^x)f[x]=find(f[x]);return f[x];
}
void combine(int u,int v){int fu=find(u),fv=find(v);if(fu^fv)f[fu]=fv;
}
struct JUX{int x1,y1,x2,y2;bool operator<(const JUX&b)const{return y1<b.y1;}
}a[MAXN];
bool check(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4){if(x2<x3||x4<x1||y2<y3||y4<y1)return 0;return 1;
}namespace zj{
#define lp p<<1
#define rp p<<1|1
#define mid ((s+t)>>1)
struct SegTree{int id,lim,typ,lazy;
}st[MAXN<<2];
void pushup(int p){if(st[lp].id==st[rp].id){st[p].id=st[lp].id;st[p].lim=st[lp].lim;st[p].typ=0;}else st[p].typ=1;if(st[lp].typ||st[rp].typ)st[p].typ=1;
}
void pushdown(int p){if(!st[p].lazy)return;st[lp].lazy=st[rp].lazy=1;st[lp].id=st[rp].id=st[p].id;st[lp].lim=st[rp].lim=st[p].lim;st[p].lazy=0;
}
void mdf(int l,int r,int lim,int id,int s=1,int t=1e5,int p=1){if(l<=s&&t<=r&&!st[p].typ){if(st[p].lim>lim)return;st[p].lazy=1,st[p].id=id,st[p].lim=lim;return;}pushdown(p);if(l<=mid)mdf(l,r,lim,id,s,mid,lp);if(mid<r)mdf(l,r,lim,id,mid+1,t,rp);pushup(p);
}
void Merge(int l,int r,int lim,int id,int s=1,int t=1e5,int p=1){if(l<=s&&t<=r&&!st[p].typ){if(st[p].lim>=lim)combine(id,st[p].id);return;}pushdown(p);if(l<=mid)Merge(l,r,lim,id,s,mid,lp);if(mid<r)Merge(l,r,lim,id,mid+1,t,rp);pushup(p);
}
int solve(){sort(a+1,a+n+1);for(int i=1;i<=n;++i){Merge(a[i].x1,a[i].x2,a[i].y1,i);mdf(a[i].x1,a[i].x2,a[i].y2,i);}int ans=0;for(int i=1;i<=n;++i)if(find(i)==i)++ans;write(ans);return fw,0;
}
}int main(){freopen("jux.in","r",stdin);freopen("jux.out","w",stdout);n=read();iota(f+1,f+n+1,1);for(int i=1;i<=n;++i)a[i]={read(),read(),read(),read()};if(n>=40000)return zj::solve();for(int i=1;i<=n;++i)for(int j=i+1;j<=n;++j)if(check(a[i].x1,a[i].y1,a[i].x2,a[i].y2,a[j].x1,a[j].y1,a[j].x2,a[j].y2))combine(i,j);int ans=0;for(int i=1;i<=n;++i)ans+=(find(i)==i);write(ans);return fw,0;
}