CSP模拟16 猜道路 简单环

我必胜

T1【floyed图论最短路】给出任意点之间的最短路,构造一个图满足条件,要求边权最小。(n<=500)

根本不用求最短路,就是固定的给出的,为了记录floyed中dis[x][y]的最短路是不是由(x,y)边自己更新的,记录一个vis,如果存在dis(x,k)+dis(k,y)<=dis(x,y)就给vis打上标记,代表(x,y)可以没有

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
    ll x=0,h=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
const int N=300+10;
int n;
ll dis[N][N],dp[N][N];
struct Node
{
    int fr,to,w;
    bool operator<(const Node&U)const
    {
        return w<U.w;
    }
}e[N*N];int tot;
int main()
{
 //  freopen("1.in","r",stdin);
  // freopen("1.out","w",stdout);
  n=re();
  ll ans=0;
  memset(dis,0x3f,sizeof(dis));
  _f(i,1,n)
  _f(j,1,n)
  {
      if(i==j)dis[i][j]=0;
      int x=re();
      if(i<j)dis[i][j]=dis[j][i]=x,e[++tot]=(Node){i,j,x};
  }
 // memcpy(dp,dis,sizeof(dis));
  sort(e+1,e+1+tot);
  _f(k,1,n)
  _f(i,1,n)
  _f(j,1,n)
  {
      if(dis[i][j]>=dis[i][k]+dis[k][j])
      {
          dis[i][j]=dis[i][k]+dis[k][j];
          if(i!=k&&k!=j)dp[i][j]=1;//是别人帮忙的
      }
  }
  _f(i,1,tot)
  {
      int f=e[i].fr,t=e[i].to,V=e[i].w;
      if(dis[f][t]<V)
      {
          chu("-1");return 0;
      }
      if(dis[f][t]==V&&!dp[f][t])ans+=V;//也有可能是别人帮忙
  }
  chu("%lld",ans);
    return 0;
}
/*
(1)并查集:
如果add_edge联通了2个块,只会影响块之间的mindis
3
0 1 3
1 0 2
3 2 0
*/

从小到大加入边,那么如果此时(x,y)的最短路>val,(x,y,val)必须加入,=不用,<不合法。
怎么在加入边之后维护最短路呢?floyedn^3会炸,去掉一层循环,会wrong,可以直接先跑DIJ把(x,y)和其他点的最短距离更新,然后再枚举通过(x,y)的点对找到mindis。

点击查看代码







#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
    ll x=0,h=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
const int N=300+10;
ll dis[N][N],ds[N],fa[N];//fa[N];//g是要求最短路,e是建立出来的边的动态最短路
int n,head[N],tot;bool vis[N][N];
struct Node
{
    int fr,to,v;
    inline bool operator<(const Node&U)const
    {
        return v<U.v;
    }
}g[N*N];int cnt;
struct DUG
{
    int to,nxt,w;
}e[N*N*2];
priority_queue<pair<ll,int> >q;
inline int getfa(int x)
{
    return (x==fa[x])?x:(fa[x]=getfa(fa[x]));
}
inline void Add(int u,int v,int w)
{
    e[++tot].to=v;
    e[tot].nxt=head[u];
    head[u]=tot;
    e[tot].w=w;
}
inline void Dij(int st)
{
   // chu("dij:%d\n",st);
    _f(i,1,n)ds[i]=0x3f3f3f3f3f3f3f3f;
    // chu("dfdij:%d  q.size(:%d\n",st,q.size());
    q.push(make_pair(0,st));
  // if(st==1)chu("didfdsfs:%d\n",st);
    while(!q.empty())
    {
        int tmp=q.top().second;
        ll das=-q.top().first;
        q.pop();
        if(ds[tmp]!=0x3f3f3f3f3f3f3f3f)continue;
        ds[tmp]=das;
       // if(st==1)chu("try:%d\n",tmp);
        for(rint i=head[tmp];i;i=e[i].nxt)
        {
            int to=e[i].to;
           // chu("%d-->%d\n",tmp,to);
            if(ds[to]>ds[tmp]+e[i].w)
            {
                q.push(make_pair(-ds[tmp]-e[i].w,to));
            }
        }
    }
    _f(i,1,n)dis[st][i]=dis[i][st]=ds[i];
}
int main()
{
  
  // freopen("1.in","r",stdin);
  // freopen("1.out","w",stdout);
   n=re();
   _f(i,1,n)
   _f(j,1,n)
   {
       int x=re();
       if(x)
       {
           if(vis[i][j])continue;
           g[++cnt]=(Node){i,j,x};
           vis[i][j]=vis[j][i]=1;
       }
   }
   sort(g+1,g+1+cnt);
  // _f(i,1,n)fa[i]=i;
  //chu("cnt:%d\n",cnt);
  memset(dis,0x3f,sizeof(dis));
  _f(i,1,n)dis[i][i]=0;
    ll tot=0;
    _f(i,1,cnt)
    {
        int V=g[i].v,f=g[i].fr,t=g[i].to;
        if(dis[f][t]<V)
        {
            chu("-1");return 0;
        }
        else if(dis[f][t]==V)continue;
        else
        {
            tot+=V;
           Add(f,t,V);Add(t,f,V);
           Dij(f);Dij(t);
           _f(x,1,n)
           _f(y,1,n)
           {
               dis[x][y]=min(dis[x][y],dis[x][f]+dis[f][y]);
               dis[x][y]=min(dis[x][y],dis[x][t]+dis[t][y]);
           }
        }
    }
    chu("%lld",tot);
    return 0;
}
/*
(1)并查集:
如果add_edge联通了2个块,只会影响块之间的mindis
3
0 1 3
1 0 2
3 2 0
*/

T2【图论】求不一定联通的图中只包含在一个简单环里的边个数,(n<=1e6)

什么样的边满足条件?如果对搜索树差分,计算出val[x]表示x的父亲边被覆盖次数,发现val[x]>1的一定是no,但是val[x]=1的也可能是yes,所以不嫩从覆盖次数角度考虑,但是因为自己dfs建立的搜索树一定只有返租边,所以使得出现覆盖的边一定是向上,所以发现如果一条返租边[x,y]包囊的边只被覆盖一定那么一定满足条件,枚举返租边树上val进行pre求和同dep验证就可以。

点击查看代码

#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
    ll x=0,h=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
const int N=1e5+100;
vector<int>son[N],gr[N];
int head[N],tot,n,m,fa[N],vis[N],dep[N],val[N],pre[N];
struct Np
{
	int to,nxt;
}e[N<<1];
map<pair<int,int>,int>mp;
vector<int>ans;
inline void Add(int x,int y)
{
	e[++tot].to=y;
	e[tot].nxt=head[x];
	head[x]=tot;
}
inline void dfs1(int x,int f)
{
	dep[x]=dep[f]+1;vis[x]=1;
	for(rint i=head[x];i;i=e[i].nxt)
	{
		int to=e[i].to;
		if(to==f)continue;
		if(!vis[to])
		{
			//chu("%d-->%d\n",x,to);
			dfs1(to,x);fa[to]=x;val[x]+=val[to];
			son[x].push_back(to);
		}
		else
		{
			if(dep[x]>dep[to])
			{
				val[x]++;val[to]--;
				gr[x].push_back(to);
			}
		}
	}
}
inline void dfs2(int x)
{
	pre[x]=(val[x]==1)+pre[fa[x]];
	for(rint to:gr[x])
	{
		if(dep[x]-dep[to]==pre[x]-pre[to])
		{
		//	chu("%d--%d %dcan\n",x,to,mp[minmax(x,to)]);
			ans.push_back(mp[minmax(x,to)]);
		//	chu("%d--%d %dcan\n",x,to,mp[minmax(x,to)]);
			int u=x,fu=fa[x];
			while(u!=to)
			{
			//	chu("u:%d\n",u);
				ans.push_back(mp[minmax(u,fu)]);
				u=fu,fu=fa[u];
			}
		}
	}
	for(rint to:son[x])dfs2(to);
}
int main()
{
   //freopen("1.in","r",stdin);
  // freopen("1.out","w",stdout);
    n=re(),m=re();
	_f(i,1,m)
	{
		int u=re(),v=re();
		Add(u,v);Add(v,u);
		mp[minmax(u,v)]=i;
	}
	_f(i,1,n)
	if(!vis[i])
	{
		dfs1(i,0);
	//	chu("df\n");
	//_f(j,1,n)chu("fa[%d]:%d\n",j,fa[j]);
		dfs2(i);
	}
	sort(ans.begin(),ans.end());
	chu("%lu\n",ans.size());
	for(rint eg:ans)
	{
		chu("%d ",eg);
	}
    return 0;
}

找环?那我把他拿出来。进行tarjan缩点,在一个点双里面的点集如果是简单环那么edge一定=dot,拿出来判断,如果满足那么所有块内的边都合法。

点击查看代码
#define sandom signed
#define fre(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout);
#include <bits/stdc++.h>
#define re register int 
using namespace std; int wrt[20], TP;
const int Z = 1e5 + 10;
inline int read() { int x = 0, f = 0; char c = getchar(); while (!isdigit(c)) f |= c == '-', c = getchar(); while (isdigit(c)) x = (x << 1) + (x << 3) + (c ^ 48), c = getchar(); return f ? -x : x; }
inline void write(int x) { TP = 0; if (x < 0) putchar('-'), x = -x; while (x >= 10) wrt[++TP] = x % 10, x /= 10; wrt[++TP] = x; while (TP) putchar(wrt[TP--] | 48); putchar(' '); }

int n, m;
vector <int> ans;
struct edge { int u, v, ne; } e[Z << 1];
int head[Z], cnt;
inline void add(int x, int y, int id) { e[id] = edge{x, y, head[x]}; head[x] = id; }
int dfn[Z], low[Z], tim, dfs[Z], top;
vector <int> vdcc[Z];
int be[Z], tot[Z], num;
void tarjan(int rt)
{
    dfn[rt] = low[rt] = ++tim;
    dfs[++top] = rt;
    for (re i = head[rt]; i; i = e[i].ne)
    {
        int son = e[i].v;
        if (!dfn[son])
        {
            tarjan(son);
            low[rt] = min(low[rt], low[son]);
            if (low[son] >= dfn[rt])
            {
                int j; num++;
                vdcc[num].push_back(rt);
                do
                {
                    j = dfs[top--];
                    vdcc[num].push_back(j);
                } while (j != son);
            }
        }
        else low[rt] = min(low[rt], dfn[son]);
    }
}
void calc(int t)
{
    for (auto rt : vdcc[t]) be[rt] = t;
    for (auto rt : vdcc[t])
        for (re i = head[rt]; i; i = e[i].ne)
            if (be[e[i].v] == t) tot[t]++;
    tot[t] >>= 1;
}
void solve(int t)
{
   for (auto rt : vdcc[t]) be[rt] = t;
    for (auto rt : vdcc[t])
        for (re i = head[rt]; i; i = e[i].ne)
            if (be[e[i].v] == t && i <= m) ans.push_back(i); 
}

sandom main()
{
    n = read(), m = read();
    for (re i = 1; i <= m; i++)
    {
        int u = read(), v = read();
        add(u, v, i), add(v, u, i + m);
    }
    for (re i = 1; i <= n; i++) if (!dfn[i]) tarjan(i);
    for (re i = 1; i <= num; i++) calc(i);//计算每个联通分量的边数
    for (re i = 1; i <= num; i++)
        if (vdcc[i].size() == tot[i]) solve(i);//点数=边数,简单环
    sort(ans.begin(), ans.end());
    write(ans.size()), putchar('\n');
    for (auto i : ans) write(i);
    return 0;
} `
posted on 2022-10-03 21:18  HZOI-曹蓉  阅读(22)  评论(0编辑  收藏  举报