NOI 1997~2003 远古题目
[NOI2002]银河英雄传说
这题就是并茶几,但是注意还要维护每个点到队尾的距离,用front[]来记前面的,不包括自己,用size[]来记每个队的大小,一减就行了,最后要减1
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=3e4+10; int n,m; int fath[maxn],fron[maxn],si[maxn]; inline int father(int x) { if(fath[x]==x) return x; int tmp=father(fath[x]); fron[x]+=fron[fath[x]]; fath[x]=tmp; return fath[x]; } inline void Union(int x,int y) { int f1=father(x),f2=father(y); if(f1!=f2) { fath[f1]=f2; fron[f1]+=si[f2]; si[f2]+=si[f1]; } } inline void deb() { printf("\n"); for(int i=1;i<=5;i++) printf("%d==",fron[i]); printf("\n"); } int main() { for(int i=1;i<=maxn-10;i++) fath[i]=i,fron[i]=0,si[i]=1; scanf("%d",&m); while(m--) { int u,v; char op; cin>>op>>u>>v; if(op=='M') Union(u,v); else { int f1=father(u),f2=father(v); if(f1!=f2) printf("-1\n"); else printf("%d\n",abs(fron[u]-fron[v])-1); } } return 0; }
[NOI2003]智破连环阵
很值得做的一道题,参考楼教主的部分搜索+二分图匹配:
https://wenku.baidu.com/view/83d4a76925c52cc58bd6beac.html?from=search
基本思想是先给武器分段,对于每一段找到可以炸完的炸弹连一条边,若全部匹配上了,就更新炸弹数,否则修改分段,还有几个剪枝:
1、用DP算出来每个武器到最后一个武器炸完所需的最少炸弹,可进行最优性剪枝
2、若当前段无法增广,直接return
3、每一次修改从以前的基础上修改,我们在枚举区间长度时,可以先用O(n^2)的广搜求出最大可攻击到的武器编号maxL,显然编号小于等于maxL的武器都可以被攻击到,这样我们保证了总有炸弹能把这一段炸完
代码:
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; const int maxn=101; int n,m,k,cnt,ans; int x[maxn],y[maxn],a[maxn],b[maxn]; int ml[maxn],mr[maxn],vis[maxn],q[maxn],dis[maxn]; int g[maxn][maxn],can[maxn][maxn][maxn],maxt[maxn][maxn]; inline int Hun(int x) { for(int i=1;i<=n;i++) if(g[x][i] && vis[i]!=cnt) { vis[i]=cnt; if(!mr[i] || Hun(mr[i])) { mr[i]=x,ml[x]=i; return 1; } } return 0; } inline int sqr(int x) { return x*x; } inline void dfs(int l,int id) { if(l>m) { ans=id-1; return; } if(id-1+dis[l]>=ans) return; ++cnt; int ML[maxn],MR[maxn],h=0,t=0,maxl=l-1; for(int i=1;i<=n;i++) if(!mr[i]) vis[i]=cnt,q[++t]=i; while(h<t) { h++; int x=q[h]; maxl=max(maxl,maxt[x][l]); for(int i=1;i<id;i++) if(g[i][x] && vis[ml[i]]!=cnt) vis[ml[i]]=cnt,q[++t]=ml[i]; } memcpy(ML,ml,sizeof(ml)); memcpy(MR,mr,sizeof(mr)); for(int i=1;i<=n;i++) g[id][i]=can[i][l][maxl]; ++cnt,Hun(id); for(int r=maxl;r>=l;r--) { for(int i=1;i<=n;i++) g[id][i]=can[i][l][r]; dfs(r+1,id+1); } memcpy(ml,ML,sizeof(ML)); memcpy(mr,MR,sizeof(MR)); } int main() { scanf("%d%d%d",&m,&n,&k); for(int i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]); for(int i=1;i<=n;i++) { scanf("%d%d",&a[i],&b[i]); for(int j=1;j<=m;j++) g[i][j]=(sqr(a[i]-x[j])+sqr(b[i]-y[j])<=k*k); } for(int p=1;p<=n;p++) { for(int i=1;i<=m;i++) can[p][i][i]=g[p][i],maxt[p][i]=g[p][i]?i:i-1; for(int i=1;i<=m;i++) for(int j=i+1;j<=m;j++) { can[p][i][j]=can[p][i][j-1]&g[p][j]; if(can[p][i][j]) maxt[p][i]=j; } } for(int i=m;i>=1;i--) { dis[i]=1e9; for(int j=i;j<=m;j++) for(int p=1;p<=n;p++) if(can[p][i][j]) dis[i]=min(dis[i],dis[j+1]+1); } ans=1e9; dfs(1,1); printf("%d\n",ans); return 0; }
[NOI2003]逃学的小孩
往最坏的情况想,他们无论如何都要走一遍两家(b,c)之间的路径,那我们就把两家放在直径的两端,现在考虑起点a,起点的贡献显然是:min( dis[a,c] , dis[a,b] ) 所以从两端分别spfa枚举a,取最大值就行了
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; const int maxn=2e5+10; struct point { int to; int nxt; long long w; }edge[maxn*2]; int n,m,tot; int head[maxn],vis[maxn]; long long disa[maxn],disb[maxn]; inline void add(int u,int v,long long w) { tot++; edge[tot].nxt=head[u]; edge[tot].to=v; edge[tot].w=w; head[u]=tot; } inline int spfa(int s,long long *dis) { queue<int> q; memset(vis,0,sizeof(vis)); vis[s]=1; for(int i=1;i<=n;i++) dis[i]=1e16; dis[s]=0; q.push(s); while(!q.empty()) { int tt=q.front(); q.pop();vis[tt]=0; for(int i=head[tt];i;i=edge[i].nxt) { int v=edge[i].to; if(dis[v]>dis[tt]+edge[i].w) { dis[v]=dis[tt]+edge[i].w; if(!vis[v]) vis[v]=1,q.push(v); } } } int who=s; for(int i=1;i<=n;i++) if(dis[i]>dis[who]) who=i; return who; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v; long long w; scanf("%d%d%lld",&u,&v,&w); add(u,v,w),add(v,u,w); } int a=spfa(1,disa); int b=spfa(a,disa); long long ans=disa[b],tmp=0; spfa(a,disa),spfa(b,disb); //for(int i=1;i<=n;i++) printf("%lld==\n",disa[i]); for(int i=1;i<=n;i++) { tmp=max(tmp,min(disa[i],disb[i])); } printf("%lld\n",ans+tmp); return 0; }