NOIP模拟2
Posted 日拱一卒 功不唐捐
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP模拟2相关的知识,希望对你有一定的参考价值。
期望得分:100+100+100=300
实际得分:70+40+20=130
T1 [SCOI2007]kshort弱化版
Description
有n个城市和m条单向道路,城市编号为1~n。每条道路连接两个不同的城市,且任意两条道路要么起点不同要么终点不同,因此n和m满足m<=n(n-1)。给定两个城市a和b,可以给a到b的所有简单路(所有城市最多经过一次,包括起点和终点)排序:先按长度从小到大排序,长度相同时按照字典序从小到大排序。你的任务是求出a到b的第k短路。
Input
输入第一行包含五个正整数n, m, k, a, b。以下m行每行三个整数u, v, l,表示从城市u到城市v有一条长度为l的单向道路。100%的数据满足:2<=n<=50, 1<=k<=200
Output
如果a到b的简单路不足k条,输出No,否则输出第k短路:从城市a开始依次输出每个到达的城市,直到城市b,中间用减号"-"分割。
基本思路:A*跑k短路
错解:重载运算符时比较字典序,直接输出第k短
错因:
正解:记录所有长度<=第k短长度的路径,取第k条
记录路径的时候,定义vector<结构体>,直接sort
vector是从0开始的!!!!
#include<queue> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define N 51 using namespace std; int n,m,k,a,b,sum; short front[N],nxt[N*N],to[N*N],tot; short front2[N],nxt2[N*N],to2[N*N],tot2; int val[N*N],val2[N*N],dis[N]; queue<int>q; struct node { int num,dis2; long long go; short cnt,road[N]; bool operator < (node p) const { return dis2+dis[num]>p.dis2+dis[p.num]; } }cur,nt; vector<node>ans; priority_queue<node>q2; bool vis[N]; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-‘0‘; c=getchar(); } } void add(int u,int v,int w) { to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w; to2[++tot2]=u; nxt2[tot2]=front2[v]; front2[v]=tot2; val2[tot2]=w; } void spfa() { memset(dis,63,sizeof(dis)); dis[b]=0; vis[b]=true; q.push(b); int now; while(!q.empty()) { now=q.front(); q.pop(); vis[now]=false; for(int i=front[now];i;i=nxt[i]) if(dis[to[i]]>dis[now]+val[i]) { dis[to[i]]=dis[now]+val[i]; if(!vis[to[i]]) { vis[to[i]]=true; q.push(to[i]); } } } } bool have(long long x,int y) { if(x&(1LL<<y)) return true; return false; } bool cmp(node h,node g) { if(h.dis2!=g.dis2) return h.dis2<g.dis2; for(int i=1;i<=min(h.cnt,g.cnt);i++) if(h.road[i]!=g.road[i]) return h.road[i]<g.road[i]; return h.cnt<g.cnt; } void solve() { if(dis[a]>2e9) { printf("No"); return; } if(a==b) k++; cur.cnt=1; cur.dis2=0; cur.go=1LL<<a; cur.num=cur.road[1]=a; q2.push(cur); while(!q2.empty()) { cur=q2.top(); q2.pop(); if(cur.num==b) { sum++; ans.push_back(cur); if(sum>k && cur.dis2>ans[k-1].dis2) break; } for(int i=front2[cur.num];i;i=nxt2[i]) if(!have(cur.go,to2[i])) { nt.cnt=cur.cnt+1; nt.dis2=cur.dis2+val2[i]; nt.go=cur.go|(1LL<<to2[i]); nt.num=to2[i]; for(int j=1;j<=cur.cnt;j++) nt.road[j]=cur.road[j]; nt.road[nt.cnt]=to2[i]; q2.push(nt); } } if(ans.size()>=k) { sort(ans.begin(),ans.end(),cmp); printf("%d",a); for(int i=2;i<=ans[k-1].cnt;i++) printf("-%d",ans[k-1].road[i]); return; } printf("No"); } int main() { read(n); read(m); read(k); read(a); read(b); int u,v,w; while(m--) { read(u); read(v); read(w); add(v,u,w); } spfa(); solve(); }
T2[ZJOI2007]最大半连通子图
Description
一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意
两点u,v,存在一条u到v的有向路径或者从v到u的有向路径。若G‘=(V‘,E‘)满足V‘?V,E‘是E中所有跟V‘有关的边,
则称G‘是G的一个导出子图。若G‘是G的导出子图,且G‘半连通,则称G‘为G的半连通子图。若G‘是G所有半连通子图
中包含节点数最多的,则称G‘是G的最大半连通子图。给定一个有向图G,请求出G的最大半连通子图拥有的节点数K
,以及不同的最大半连通子图的数目C。由于C可能比较大,仅要求输出C对X的余数。
Input
第一行包含两个整数N,M,X。N,M分别表示图G的点数与边数,X的意义如上文所述接下来M行,每行两个正整
数a, b,表示一条有向边(a, b)。图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次。N ≤1
00000, M ≤1000000;对于100%的数据, X ≤10^8
Output
应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.
tarjan缩环,然后拓扑排序求最长链
注意缩环之后重新构图会出现重边,在拓扑排序里计算方案数时会多算
所以拓扑排序时判断一下是否被同一节点重复更新
错因:没有想到重边,存边的数组大小与点混淆
#include<cstdio> #include<iostream> #include<algorithm> #define N 100001 #define M 1000001 using namespace std; int mod,tot,cnt; int front[N],nxt[M],to[M],from[M]; int front2[N],nxt2[M],to2[M],ru[N]; int dp[N][2],maxn,ans; int dfn[N],low[N],col[N],siz[N],st[N],top; bool vis[N]; int last[N]; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-‘0‘; c=getchar(); } } void add(int u,int v) { to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; from[tot]=u; } void add2(int u,int v) { to2[++tot]=v; nxt2[tot]=front2[u]; front2[u]=tot; ru[v]++; } void tarjan(int now) { low[now]=dfn[now]=++tot; st[++top]=now; vis[now]=true; for(int i=front[now];i;i=nxt[i]) { if(!dfn[to[i]]) { tarjan(to[i]); low[now]=min(low[now],low[to[i]]); } else if(vis[to[i]]) low[now]=min(low[now],dfn[to[i]]); } if(low[now]==dfn[now]) { cnt++; while(top && st[top]!=now) { col[st[top]]=cnt; vis[st[top]]=false; top--; } col[now]=cnt; vis[now]=false; top--; } } void topsort() { top=0; for(int i=1;i<=cnt;i++) if(!ru[i]) { st[++top]=i; dp[i][0]=siz[i]; dp[i][1]=1; } int now; while(top) { now=st[top]; top--; for(int i=front2[now];i;i=nxt2[i]) { if(dp[now][0]+siz[to2[i]]>dp[to2[i]][0]) { dp[to2[i]][0]=dp[now][0]+siz[to2[i]]; dp[to2[i]][1]=dp[now][1]; last[to2[i]]=now; } else if(dp[now][0]+siz[to2[i]]==dp[to2[i]][0]) { if(last[to2[i]]==now) { ru[to2[i]]--; if(!ru[to2[i]]) st[++top]=to2[i]; continue; } last[to2[i]]=now; dp[to2[i]][1]+=dp[now][1]; dp[to2[i]][1]%=mod; } ru[to2[i]]--; if(!ru[to2[i]]) st[++top]=to2[i]; } } } int main() { int n,m; read(n); read(m); read(mod); int u,v; for(int i=1;i<=m;i++) { read(u); read(v); add(u,v); } tot=0; for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=n;i++) siz[col[i]]++; tot=0; for(int i=1;i<=m;i++) if(col[from[i]]!=col[to[i]]) add2(col[from[i]],col[to[i]]); topsort(); for(int i=1;i<=cnt;i++) maxn=max(maxn,dp[i][0]); for(int i=1;i<=cnt;i++) if(maxn==dp[i][0]) ans+=dp[i][1],ans%=mod; printf("%d\n%d",maxn,ans); } ?
T3[AHOI2006]上学路线route
Description
Input
Output
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<queue> #define N 501 #define M 200001 using namespace std; int n,m; int dis[N][N]; int e[M][4]; int src,decc; int tot=1,front2[N],to2[M<<1],nxt2[M<<1],cap[M<<1],lev[N],cur[N]; bool vis[N]; queue<int>q; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-‘0‘; c=getchar(); } } void add2(int u,int v,int w) { to2[++tot]=v; nxt2[tot]=front2[u]; front2[u]=tot; cap[tot]=w; to2[++tot]=u; nxt2[tot]=front2[v]; front2[v]=tot; cap[tot]=0; } void build() { for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(k!=i && i!=j && k!=j) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); int minn=dis[1][n]; printf("%d\n",minn); for(int i=1;i<=m;i++) { if(dis[1][e[i][0]]+e[i][2]+dis[e[i][1]][n]==minn) add2(e[i][0],e[i][1],e[i][3]); if(dis[1][e[i][1]]+e[i][2]+dis[e[i][0]][n]==minn) add2(e[i][1],e[i][0],e[i][3]); } } bool spfa2() { for(int i=1;i<=n;i++) cur[i]=front2[i],lev[i]=-1; while(!q.empty()) q.pop(); lev[1]=0; q.push(1); int now; while(!q.empty()) { now=q.front(); q.pop(); for(int i=front2[now];i;i=nxt2[i]) if(lev[to2[i]]==-1 && cap[i]>0) { lev[to2[i]]=lev[now]+1; if(to2[i]==n) return true; q.push(to2[i]); } } return false; } int dinic(int now,int flow) { if(now==n) return flow; int delta,rest=0; for(int & i=cur[now];i;i=nxt2[i]) if(lev[to2[i]]>lev[now] && cap[i]>0) { delta=dinic(to2[i],min(cap[i],flow-rest)); if(delta) { cap[i]-=delta; cap[i^1]+=delta; rest+=delta; if(rest==flow) break; } } if(rest!=flow) lev[now]=-1; return rest; } int main() { read(n); read(m); int u,v,t,c; memset(dis,63,sizeof(dis)); for(int i=1;i<=n;i++) dis[i][i]=0; for(int i=1;i<=m;i++) { read(u); read(v); read(t); read(c); e[i][0]=u; e[i][1]=v; e[i][2]=t; e[i][3]=c; dis[u][v]=dis[v][u]=min(dis[u][v],t); } build(); int ans=0; while(spfa2()) ans+=dinic(1,2e9); printf("%d",ans); }
以上是关于NOIP模拟2的主要内容,如果未能解决你的问题,请参考以下文章