5.6水题合集
T1:杭州旅行
floyd 求最小环,相当于枚举环上编号最大的点进行转移
正确性:
一个环中的最大结点为k(编号最大),与他相连的两个点为i,j,这个环的最短长度为g[i][k]+g[k][j]+i到j的路径中,所有结点编号都小于k的最短路径长度
根据floyd的原理,在最外层循环做了k-1次之后,dist[i][j]则代表了i到j的路径中,所有结点编号都小于k的最短路径
综上所述,该算法一定能找到图中最小环
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define RG register
using namespace std;
const int N=850;
const int Inf=19260817;
int gi(){
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
int f[N][N],w[N][N],n,m;
int main(){
freopen("zdlcs.in","r",stdin);
freopen("zdlcs.out","w",stdout);
n=gi(),m=gi();
for(RG int i=1;i<=n;i++){
for(RG int j=1;j<=n;j++){
if(i!=j) f[i][j]=w[i][j]=Inf;
}
}
for(int i=1;i<=m;i++){
int x=gi(),y=gi(),z=gi();
w[x][y]=min(w[x][y],z);
w[y][x]=f[y][x]=f[x][y]=w[x][y];
}
int ans=Inf;
for(RG int k=1;k<=n;k++){
for(RG int i=1;i<k;i++)
for(RG int j=1;j<i;j++){
ans=min(ans,f[i][j]+w[k][i]+w[k][j]);
}
for(RG int i=1;i<=n;i++){
for(RG int j=1;j<=n;j++){
if(k!=i&&k!=j) f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
}
}
}
if(ans==Inf) puts("It's impossible.");
else printf("%d\n",ans);
}
T2:奇偶性游戏
跟狡猾的商人一模一样,多了一个离散化,把+变成异或即可
做法:如果知道[L,R],[L,r],那么[r-1,R]的奇偶就可以确定后,所以只要动态维护合并每个连通块中点到根的距离
如果已经在同一个连通块中就可以直接判断
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define RG register
using namespace std;
const int N=100000;
struct data {
int l,r,c;
} q[N];
int fa[N],sum[N],hsh[N],n,Q,tot;
char s[10];
int gi(){
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline int find(RG int x) {
if (fa[x]==x) return x;
RG int u=find(fa[x]);
sum[x]^=sum[fa[x]];
return fa[x]=u;
}
inline void work() {
n=gi(),Q=gi();
for (RG int i=1; i<=Q; ++i) {
q[i].l=gi()-1,q[i].r=gi();
hsh[++tot]=q[i].l,hsh[++tot]=q[i].r;
scanf("%s",s),q[i].c=s[0]=='o';
}
sort(hsh+1,hsh+tot+1),tot=unique(hsh+1,hsh+tot+1)-hsh-1;
for (RG int i=1;i<=tot;i++) fa[i]=i;
for (RG int i=1;i<=Q;i++) {
q[i].l=lower_bound(hsh+1,hsh+tot+1,q[i].l)-hsh;
q[i].r=lower_bound(hsh+1,hsh+tot+1,q[i].r)-hsh;
RG int x=find(q[i].l),y=find(q[i].r);
if (x==y && (sum[q[i].l]^sum[q[i].r])!=q[i].c) {
printf("%d\n",i-1);
return;
}
if (x>y) swap(x,y);
fa[y]=x,sum[y]=sum[q[i].l]^sum[q[i].r]^q[i].c;
}
printf("%d\n",Q);
return;
}
int main() {
freopen("parity.in","r",stdin);
freopen("parity.out","w",stdout);
work();
return 0;
}
T3:膜拜神犇
考虑这种有向图求环之类的问题考虑tarjan;
缩完点后原图变为一个DAG,考虑缩完点后的图中的点的情况只有三种:
1.1号节点所在的强连通分量可以到达,称之为一类
2.能到达1号节点所在的强连通分量,称之为二类
3.跟1号节点所在的强连通分量没啥关系
第三中可以省略;考虑答案的构成,
必定由1号点所在的强连通分量走到一个一类点,再选一个与这个一类点相连的二类点,把它们之间的这条边选择反向走.
(这条边本来必然是由二类点指向一类点的,否则,1号结点所在的强连通分量必然可以到达这个二类点,
而二类点由是可以到达1号点所在的强连通分量的,这样就有环了,而DAG是无环图)
然后再从这个二类点走到一号点所在的强连通分量.
于是我们只需要求出
1.1号节点所在的强连通分量到每个一类点的MAX_dis;
2.每个二类点到1号节点所在的强连通分量的MAX_dis(这个实现的话把边全部反过来,变为求1到它们的距离)
然后把他们组合起来再减去1号节点所在的强连通分量的大小
DAG的最大权值路径可直接spfa;
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#define RG register
using namespace std;
const int N=100010;
const int Inf=19260817;
int tt,tim,cnt,tp;
bool w[N];
int head[N],to[N],nxt[N],dfn[N],low[N],zhan[N],fr[N],size[N],n,m;
int dis[N][3],q[N*20];
vector<int>p[N][3];
inline int gi() {
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void dfs(int x) {
dfn[x]=low[x]=++tim;
zhan[++tp]=x,w[x]=1;
for(RG int i=head[x];i;i=nxt[i]) {
int y=to[i];
if(!dfn[y]) {
dfs(y);low[x]=min(low[x],low[y]);
} else if(w[y]) low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]) {
int y;cnt++;
do {
y=zhan[tp--];
w[y]=0,fr[y]=cnt,size[cnt]++;
} while(y!=x);
}
}
inline void spfa(int x){
dis[fr[1]][x]=size[fr[1]];int t=0,sum=1;q[0]=fr[1];
while(t<sum){
int now=q[t++];
for(RG int i=0;i<p[now][x].size();i++){
int y=p[now][x][i];
if(dis[y][x]<dis[now][x]+size[y]){
dis[y][x]=dis[now][x]+size[y];
q[sum++]=y;
}
}
}
}
int main() {
freopen("OrzOrz.in","r",stdin);
freopen("OrzOrz.out","w",stdout);
n=gi(),m=gi();
for(RG int i=1;i<=m;i++) {
int x=gi(),y=gi();
to[++tt]=y,nxt[tt]=head[x],head[x]=tt;
}
for(RG int i=1;i<=n;i++) if(!dfn[i]) dfs(i);
for(RG int i=1;i<=n;i++)
for(RG int j=head[i];j;j=nxt[j])
if(fr[to[j]]!=fr[i]){
p[fr[i]][1].push_back(fr[to[j]]);
p[fr[to[j]]][2].push_back(fr[i]);
}
spfa(1);spfa(2);int ans=0;
for(RG int i=1;i<=cnt;i++){
for(RG int j=0;j<p[i][2].size();j++){
int y=p[i][2][j];
if(dis[i][1]!=0&&dis[y][2]!=0){
ans=max(ans,dis[i][1]+dis[y][2]);
}
}
}
printf("%d\n",ans-size[fr[1]]);
return 0;
}
T4: PG图
最小值最大考虑二分;
对于每一条边:w[i]+sum[u]-sum[v]>=mid;
sum[v]-sum[u]<=w[i]-mid;与最短路的松弛操作相似,用差分约束check;
于是从v向u连一条w[i]-mid的边,判断是否有负权环即可.
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1000,M=3000;
int head[N],to[M],nxt[M],s[M],c[M],dis[N],vis[N],n,m;
struct Data {
int x,y,z;
} g[M];
int gi() {
int x=0,flag=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-'){flag=-1;}ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*flag;
}
bool spfa(int x) {
for(int i=head[x];i;i=nxt[i]) {
int y=to[i];
if(dis[y]>dis[x]+s[i]) {
dis[y]=dis[x]+s[i];
if(vis[y]++>100||spfa(y)) return 1;
}
}
return 0;
}
int main() {
freopen("pg.in","r",stdin);
freopen("pg.out","w",stdout);
while(scanf("%d%d",&n,&m)!=EOF) {
memset(head,0,sizeof(head));
for(int i=1;i<=m;i++) {
int x=gi(),y=gi();c[i]=gi();
to[i]=y,nxt[i]=head[x],head[x]=i;
}
int l=0,r=10001;
while(l<=r) {
int mid=(l+r)>>1;int flag=0;
for(int i=1;i<=m;i++) s[i]=c[i]-mid;
for(int i=1;i<=n;i++) {
memset(dis,127,sizeof(dis));
memset(vis,0,sizeof(vis));
if(spfa(i)) {flag=1;break;};
}
if(flag) r=mid-1;
else l=mid+1;
}
if(r<0) puts("No Solution");
else if(r==10001) puts("Infinite");
else printf("%d\n",r);
}
return 0;
}

浙公网安备 33010602011771号