图论_3。

Instant Noodles呃 ԅ(¯﹃¯ԅ)看了眼题解,懂了吧就根据三种情况来判断在这里插入图片描述
快!看!这个合并,其实是很简单的,就如果重复了,合并,如果没用重复,分两种情况讨论一下即可(虽然最终的殊途同归)。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,len=0,last[500001],c[500001];
set<int > g[500001];
map<set<int >,int > mp;
signed main()
{
	int t;scanf("%lld",&t);
	while(t--)
	{
		mp.clear();
		scanf("%lld%lld",&n,&m);
		for(int i=1;i<=n;i++) scanf("%lld",&c[i]);
		for(int i=1;i<=n;i++) g[i].clear();
		while(m--)
		{
			int x,y;scanf("%lld%lld",&x,&y);
			g[y].insert(x);
		}
		for(int i=1;i<=n;i++)
		{
			if(g[i].size()) mp[g[i]]+=c[i];
		}
		int ans=0;
		for(auto &i:mp)
            if(ans) ans=__gcd(ans,i.second);
            else ans=i.second;
        printf("%lld\n",ans);
 	}
	return 0;
}

这一拳,天昏地暗。Weights Distributing,迷茫起来了吗?一切还好吗?也许再也难见,便也笑着再见了。好!说题目,不知道是什么的图论,求 a 到 b 的最短路与 b 到 c 的最短路的和的最小值。不妨想想只有两个点的情况,那么简单嘛。再看看三个点,那就麻烦了因为会有重叠的情况,那么画出路径,大概是a->i->b,b->i->c,这样的,枚举i这样最小值就是在这里插入图片描述
那么就可以放心维护了,最好搞一个前缀和什么的方便维护。直接统计路径来干就行,理解就好。
不过这个前缀和非常奇妙,学习一下?呃因为这题卡常我也没办法,先抄了别人的题解,我代码应该是没问题的。不过t了。

#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,last[100001],w[100001],q[100001];
int dis[100001],disa[100001],disb[100001],disc[100001];
struct pp
{
	int x,y,next;
};pp p[4000001];
void ins(int x,int y)
{
	int now=++len;
	p[now]={x,y,last[x]};last[x]=now;
	return ;
}
void bfs(int ST,int op)
{
	memset(dis,0,sizeof(dis));dis[ST]=1;
	int st=1,ed=2;q[st]=ST;
	while(st!=ed)
	{
		int x=q[st++];
		for(int i=last[x];i!=-1;i=p[i].next)
		{
			int y=p[i].y;if(dis[y]) continue ;
			dis[y]=dis[x]+1;q[ed++]=y;
		}
	} 
	if(op==1) for(int i=1;i<=n;i++) disa[i]=dis[i]-1;
	if(op==2) for(int i=1;i<=n;i++) disb[i]=dis[i]-1;
	if(op==3) for(int i=1;i<=n;i++) disc[i]=dis[i]-1;
	return ;
}
bool cmp(const int &x,const int &y)
{
	return x<y;
}
int main()
{
	int t;scanf("%d",&t);
	while(t--)
	{
		memset(last,-1,sizeof(last));len=0;
		int a,b,c;scanf("%d%d%d%d%d",&n,&m,&a,&b,&c);
		for(int i=1;i<=m;i++) scanf("%d",&w[i]);
		sort(w+1,w+m+1,cmp);
		for(int i=2;i<=m;i++) w[i]+=w[i-1];
		for(int i=1;i<=m;i++)
		{
			int x,y;scanf("%d%d",&x,&y);
			ins(x,y);ins(y,x);
		}
		bfs(a,1);bfs(b,2);bfs(c,3);int ans=1e9;
		for(int i=1;i<=n;i++) 
		{
			int A=disa[i],B=disb[i],C=disc[i];
			if(A+B+C<=m) ans=min(ans,w[A+B+C]+w[B]);
		}
		printf("%d\n",ans);
	}
	return 0;
}

String Transformation 2,呃非常奇妙的做法,真的。https://blog.csdn.net/sharp_legendgod/article/details/119521342可以考虑一下看大佬的题解
在这里插入图片描述
在这里插入图片描述
题是写了,想法以后能补再补吧。

#include <bits/stdc++.h>
using namespace std;

//#define Fread
// #define Getmod

#ifdef Fread
char buf[1 << 21], *iS, *iT;
#define gc() (iS == iT ? (iT = (iS = buf) + fread (buf, 1, 1 << 21, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
#endif // Fread

template <typename T>
void r1(T &x) {
	x = 0;
	char c(getchar());
	int f(1);
	for(; c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(; '0' <= c && c <= '9';c = getchar()) x = (x * 10) + (c ^ 48);
	x *= f;
}

const int mod  = 1e9 + 7;
#ifdef Getmod
template <int mod>
struct typemod {
    int z;
    typemod(int a = 0) : z(a) {}
    inline int inc(int a,int b) const {return a += b - mod, a + ((a >> 31) & mod);}
    inline int dec(int a,int b) const {return a -= b, a + ((a >> 31) & mod);}
    inline int mul(int a,int b) const {return 1ll * a * b % mod;}
    typemod<mod> operator + (const typemod<mod> &x) const {return typemod(inc(z, x.z));}
    typemod<mod> operator - (const typemod<mod> &x) const {return typemod(dec(z, x.z));}
    typemod<mod> operator * (const typemod<mod> &x) const {return typemod(mul(z, x.z));}
    typemod<mod>& operator += (const typemod<mod> &x) {*this = *this + x; return *this;}
    typemod<mod>& operator -= (const typemod<mod> &x) {*this = *this - x; return *this;}
    typemod<mod>& operator *= (const typemod<mod> &x) {*this = *this * x; return *this;}
    int operator == (const typemod<mod> &x) const {return x.z == z;}
    int operator != (const typemod<mod> &x) const {return x.z != z;}
};
typedef typemod<mod> Tm;
#endif
template <typename T,typename... Args> inline void r1(T& t, Args&... args) {
    r1(t);  r1(args...);
}

// #define int long long
const int maxn = 20 + 5;
const int maxm = 1e5 + 5;
int vis[maxn];
int n;
char A[maxm], B[maxm];
int vc1[maxn][maxn];
void add(int u,int v) {
    ++ u, ++ v;
    vc1[u][v] = 1;
}
int vc[maxn][maxn];
int st[maxn], ed(0);
int E[maxn];
void dfs(int p) {
    vis[p] = 1, st[ed ++] = p;
    for(int j = 1; j <= 20; ++ j) if(vc1[p][j] && !vis[j]) dfs(j);
}

#define Online
signed main() {
#ifndef Online
    freopen("S.in", "r", stdin);
    freopen("S.out", "w", stdout);
#endif

    auto calc = [&] (void) {
        const int inf = 0x3f3f3f3f;
        int z = (1 << ed) - 1, i, j;
        for(i = 0; i < ed; ++ i) {
            E[i] = 0;
            for(j = 0; j < ed; ++ j) E[i] |= vc[st[i]][st[j]] << j;
        }
        vector<int> dp(z + 1, inf);
        dp[0] = 0;
        for(int S = 0; S <= z; ++ S) {
            for(int j = 0; j < ed; ++ j) if(!((S >> j) & 1)) {
                dp[S | (1 << j)] = min(dp[S | (1 << j)], dp[S] + !!(S & E[j]));
            }
        }
//        printf("dp[%lld] = %lld\n", ed, dp[z]);
        return dp[z] + ed - 1;
    };

    auto Solve = [&] (void) {
        int i, j;
        memset(vis, 0, sizeof(vis));
        memset(vc, 0, sizeof(vc));
        memset(vc1, 0, sizeof(vc1));
        r1(n);
        scanf("%s%s", A + 1, B + 1);
        for(i = 1; i <= n; ++ i) {
            vc[A[i] - 'a' + 1][B[i] - 'a' + 1] = 1;
            add(A[i] - 'a', B[i] - 'a'), add(B[i] - 'a', A[i] - 'a');
        }
        int ans(0);
        for(i = 1; i <= 20; ++ i) if(!vis[i]) {
            ed = 0;
            dfs(i);
            ans += calc();
//            printf("i = %lld\n", i);
        }
        printf("%d\n", ans);
//        puts("----\n");
    };

    int T;
    r1(T);
    while(T --) Solve();

	return 0;
}
/*
1
3
abc
tsr

3

*/


Johnny and Contribution

#include<bits/stdc++.h> 
using namespace std;
int n,m,len=0,last[5020001];
bool v[5020001];
struct pp
{
	int x,y,next;
};pp p[1020001];
struct node
{
	int t,id;
};node e[5020001];
void ins(int x,int y)
{
	int now=++len;
	p[now]={x,y,last[x]};last[x]=now;
	return ;
}
bool cmp(const node &x,const node &y)
{
	return x.t<y.t;
}
int main()
{
	memset(last,-1,sizeof(last));memset(v,true,sizeof(v));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int x,y;scanf("%d%d",&x,&y);
		ins(x,y);ins(y,x); 
	}
	for(int i=1;i<=n;i++) scanf("%d",&e[i].t),e[i].id=i;
	for(int i=1;i<=n;i++)
	{
		for(int j=last[i];j!=-1;j=p[j].next) v[e[p[j].y].t]=false ;
		for(int j=1;j<e[i].t;j++) 
		{
			if(v[j]) 
			{
				printf("-1");
				return 0;
			}
		}
		if(!v[e[i].t]) 
		{
			printf("-1");
			return 0;
		}
		for(int j=last[i];j!=-1;j=p[j].next) v[e[p[j].y].t]=true ;
	}
	sort(e+1,e+n+1,cmp);
	for(int i=1;i<=n;i++) printf("%d ",e[i].id);
	return 0;
}

告一段落!
不不不第二阶段重启!P1948 [USACO08JAN]Telephone Lines S

//一看,求最大值最小,不妨考虑二分答案。然后用dj维护一下,若一条边的长度超过了上限
//那么计数器++最后判断计数器<=k就好了 
#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,len=0,last[100001],dis[100001],k;
struct pp
{
	int x,y,c,next;
};pp p[1000001];
void ins(int x,int y,int c)
{
	int now=++len;
	p[now]={x,y,c,last[x]};last[x]=now; 
	return ;
}
int q[100001];bool v[100001];
bool spfa(int ST,int ED,int lim)
{
	memset(v,true,sizeof(v));memset(dis,63,sizeof(dis));dis[ST]=0;
	int st=1,ed=2;q[st]=ST;
	while(st!=ed)
	{
		int x=q[st++];v[x]=true;
		for(int i=last[x];i!=-1;i=p[i].next)
		{
			int y=p[i].y,s=dis[x];if(p[i].c>lim) s++;
			if(dis[y]<=s) continue ;
			dis[y]=s;if(v[y]) q[ed++]=y,v[y]=false;  
		}
	}
	if(dis[ED]==dis[0]) 
	{
		printf("-1");exit(0);
	}
	if(dis[ED]<=k) return true;
	else return false;
}
 main()
{
	memset(last,-1,sizeof(last));
	
	scanf("%lld%lld%lld",&n,&m,&k);int l=0,r=0,ans,ST=1,ED=n;
	for(int i=1;i<=m;i++)
	{
		int x,y,c;scanf("%lld%lld%lld",&x,&y,&c);
		ins(x,y,c);ins(y,x,c);r=max(r,c);
	}
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(spfa(ST,ED,mid)) r=mid-1,ans=mid;
		else l=mid+1;
	}
	printf("%lld",ans);
	return 0;
 } 

P1407 [国家集训队]稳定婚姻在这里插入图片描述

//呃考虑从建边入手,不妨将婚姻的人g->b,情人的b->g,那么就可以得到一个有向有环图。
//那么再一个联通块里面的点,那么肯定可以换npy了,反之则不行 
#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,last[100001],low[100001],dfsx[100001],dfs_x=0,id[100001],point=0;
int q[100001],tot=0;bool v[100001];
map<string ,int > mp;
struct pp
{
	int x,y,next;
};pp p[4000001];
void ins(int x,int y)
{
	int now=++len;
	p[now]={x,y,last[x]};last[x]=now;
	return ;
}
void SH(int x)
{
	dfsx[x]=low[x]=++dfs_x;
	v[x]=true;q[++tot]=x;
	for(int i=last[x];i!=-1;i=p[i].next)
	{
		int y=p[i].y;
		if(!dfsx[y]) SH(y),low[x]=min(low[x],low[y]);
		else if(v[y]) low[x]=min(low[x],dfsx[y]);
	}
	if(dfsx[x]==low[x])
	{
		point++;
		while(dfsx[q[tot]]>=dfsx[x])
		{
			id[q[tot]]=point;v[q[tot]]=false;
			tot--;dfs_x--;
		}
	}
	return ;
}
int main()
{
	memset(last,-1,sizeof(last));memset(v,false,sizeof(v));
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		string s1,s2;cin>>s1>>s2;
		mp[s1]=i,mp[s2]=i+n;ins(i,i+n);
	}
	scanf	("%d",&m);
	for(int i=1;i<=m;i++)
	{
		string s1,s2;cin>>s1>>s2;
		ins(mp[s2],mp[s1]);
	}
	for(int i=1;i<=n*2;i++) if(!dfsx[i]) SH(i);
	for(int i=1;i<=n;i++)
	{
		if(id[i]==id[i+n]) printf("Unsafe\n");
		else printf("Safe\n");
	}
	
	return 0;
}

P1640 [SCOI2010] 连续攻击游戏二分图的优化版,可以尝试用时间戳来优化memset,具体而言,设一个变量now,如果v里值为now则已访问过,若不是则赋值为now向下递归,这样就不用memset v数组了。然后二分图的思路是挺好想的因为要求必须从1开始,所以我们直接从1向上枚举即可。

#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,last[2000001],v[2000001],cnt=0,op[2000001];
struct pp
{
	int x,y,next;	
};pp p[8000001];
void ins(int x,int y)
{
	int now=++len;
	p[now]={x,y,last[x]};last[x]=now;
	return ;
}
bool  dfs(int x)
{
	for(int i=last[x];i!=-1;i=p[i].next)
	{
		int y=p[i].y;if(v[y]==cnt) continue ;v[y]=cnt;	
		if(!op[y]||dfs(op[y])) 
		{
			op[y]=x,op[x]=y;
			return true ;
		}
	}
	return false;
}
int main()
{
	memset(last,-1,sizeof(last));memset(v,0,sizeof(v));
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=2;j++) 
		{
			int x;scanf("%d",&x);
			ins(x,i+100001);ins(i+100001,x);
		}
	}int ans=0;
	for(int i=1;i<=100001;i++)
	{
		cnt++;
		if(dfs(i)) ans++;
		else break;
	}printf("%d",ans);
	return 0;
}

矩阵加速弗洛伊德,问题在于转化负权,我们不如直接看题解。P6190 [NOI Online #1 入门组] 魔法

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,len,f[521][521];
struct pp
{
	int x,y,c;
};pp p[1000001];
struct node
{
	int n,m,w[121][121];
	friend node operator *(const node &x,const node &y)
	{
		node rt;rt.n=x.n,rt.m=y.m;memset(rt.w,0x3f,sizeof(rt.w));
		for(int i=1;i<=rt.n;i++)
		{		
			for(int j=1;j<=rt.m;j++)
			{
				for(int k=1;k<=x.m;k++) rt.w[i][j]=min(rt.w[i][j],x.w[i][k]+y.w[k][j]);//printf("%d\n",rt.w[i][j]);
			}
		}
		return rt;
	};
};node A,B;
void init(node &now,int x)
{
	now.n=now.m=x;memset(now.w,63,sizeof(now.w));
	for(int i=1;i<=x;i++) now.w[i][i]=0;
	return ;
}
node power(node x,int k)
{
	node rt;init(rt,n);
	while(k)
	{
		if(k&1) rt=rt*x;
		x=x*x;k>>=1;
	}
	return rt; 
}

signed main()
{
	memset(f,0x3f,sizeof(f));
	int k;scanf("%lld%lld%lld",&n,&m,&k);A.n=A.m=n;
	for(int i=1;i<=m;i++)
	{
		int x,y,c;scanf("%lld%lld%lld",&x,&y,&c);
		p[i].x=x,p[i].y=y,p[i].c=c;f[x][y]=c;
	}
	for(int i=1;i<=n;i++) f[i][i]=0;
	for(int k=1;k<=n;k++)
	{
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++) f[i][j]=min(f[i][j],f[i][k]+f[k][j]);	
		}
	}
	memset(A.w,0x3f,sizeof(A.w));
	for(int i=1;i<=m;i++)
	{
		int x=p[i].x,y=p[i].y,c=p[i].c;
		for(int j=1;j<=n;j++)
		{
			for(int k=1;k<=n;k++) A.w[j][k]=min(A.w[j][k],min(f[j][k],f[j][x]+f[y][k]-c));
		}
	}
//	for(int i=1;i<=n;i++) A.w[i][i]=0;
	if(!k) printf("%lld\n",f[1][n]);
	else 
	{
		node ans=power(A,k);
		printf("%lld\n",ans.w[1][n]);
	} 
	return 0;
}

P1606 [USACO07FEB]Lilypad Pond G,先做一道前置的最短路计数题目P1608 路径统计,不难就当复习了。

#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,len=0,last[100001],dis[10001],ans[100001];bool v[10001],mp[2301][2301];
struct pp
{
	int x,y,c,next;
};pp p[4000001];
void ins(int x,int y,int c)
{
	int now=++len;
	p[now]={x,y,c,last[x]};last[x]=now;
	return ;
}
struct node 
{
	int x,dis;friend bool operator < (const node &x,const node &y)
	{
		return x.dis > y.dis;
	};
};priority_queue<node > q;
bool dj(int ST,int ED)
{
	memset(dis,63,sizeof(dis));memset(v,true,sizeof(v));memset(ans,0,sizeof(ans));dis[ST]=0;
	node e;e.dis=dis[ST];e.x=ST;q.push(e);ans[ST]=1;
	while(q.size())
	{
		node x=q.top();q.pop();if(!v[x.x]) continue ;v[x.x]=false;
		for(int i=last[x.x];i!=-1;i=p[i].next)
		{
			int y=p[i].y;if(dis[y]<dis[x.x]+p[i].c) continue ;
			if(dis[y]==dis[x.x]+p[i].c) ans[y]+=ans[x.x];
			else ans[y]=ans[x.x],dis[y]=dis[x.x]+p[i].c,e.dis=dis[y],e.x=y,q.push(e);
		}
	}
	if(dis[0]==dis[ED]) return false;
	else return true ;
}
 main()
{
	memset(last,-1,sizeof(last));memset(mp,true,sizeof(mp));
	scanf("%lld%lld",&n,&m);	
	for(int k=1;k<=m;k++)
	{
		int x,y,c;scanf("%lld%lld%lld",&x,&y,&c);
		if(mp[x][y]) ins(x,y,c),mp[x][y]=false;
		else
		{
			for(int i=last[x];i!=-1;i=p[i].next)
			{
				int yy=p[i].y;if(y!=yy) continue ;
				p[i].c=min(p[i].c,c);break;
			}
		}
	}
	if(!dj(1,n)) printf("No answer");	
	else printf("%lld %lld\n",dis[n],ans[n]);
	return 0;
}

好的我们选择跳过上面那道题:P4042 [AHOI2014/JSOI2014]骑士游戏加深一下对spfa的理解。好吧其实不看题解的话我不打算用spfa做的,我应该先考虑拓扑,发现不行,然后考虑dp或者记忆化。仔细想想,这一题需要松弛。

//不妨尝试用改版的spfa,如果我儿子节点的值被更新后能更新我自己,那么我就更新我的父亲
//但是这样的时间复杂度难以保证 
#include<bits/stdc++.h>
#define int long long 
using namespace std;
int n,m,len=0,last[500001],dis[500001],a[500001],lastfa[500001];
struct pp
{
	int x,y,next;
};pp fa[2000001],p[2000001];
void ins(int x,int y)
{
	int now=++len;
	p[now]={x,y,last[x]};last[x]=now;
	return ;
}
void insfa(int x,int y)
{
	int now=++len;
	fa[now]={x,y,lastfa[x]};lastfa[x]=now;
	return ;
}
int q[1000001];bool v[500001];
void spfa()
{
	int st=1,ed=1;
	for(int i=1;i<=n;i++) q[ed++]=i;
	while(st!=ed)
	{
		int x=q[st++];v[x]=false;
		int tmp=a[x];
		for(int i=last[x];i!=-1;i=p[i].next) tmp+=dis[p[i].y];
		if(dis[x]>tmp)
		{
			dis[x]=tmp;
			for(int i=lastfa[x];i!=-1;i=fa[i].next)
			{
				if(!v[fa[i].y]) q[ed++]=fa[i].y,v[fa[i].y]=true;  
			}
		}
	}
	return ;
}
 main()
{
	memset(last,-1,sizeof(last));memset(lastfa,-1,sizeof(lastfa));
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		int x;scanf("%lld%lld%lld",&a[i],&dis[i],&x);
		while(x--) 
		{
			int y;scanf("%lld",&y);
			ins(i,y),insfa(y,i);
		}
	}
	spfa();printf("%lld",dis[1]);
	return 0;
}

MST Unificationluogu炸了所以换了个地方交

//求解唯一最小生成树,我们考虑一遍建树一遍做,那么不妨观察对于一个树边,要是能被替代的话肯定是有另一条有相同值且可以与其构成环的边
//那么我们的解决方案是让那个边的值加一,所以我们只需要在寻找的过程中,一边建树一边算.
//先算相同边的数量,若之前就在同一个块里面那就不统计
//然后再算组成环的数量,那么就要减去一部分。 
#include<bits/stdc++.h>
using namespace std;
int n,m,len=0,maxx[1000001],fa[1000001],ans=0;
struct pp
{
	int x,y,c;
};pp p[1000001];
bool cmp(const pp &x,const pp &y)
{
	return x.c<y.c;
}
int findfa(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=findfa(fa[x]);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].c);
	for(int i=1;i<=n;i++) fa[i]=i;sort(p+1,p+m+1,cmp);
	int i=1;  
	while(i<=m)
	{
		int j=i;while(p[j].c==p[j+1].c) j++;
		for(int k=i;k<=j;k++) if(findfa(p[k].x)!=findfa(p[k].y)) ans++;
		for(int k=i;k<=j;k++) 
		{
			int fx=findfa(p[k].x),fy=findfa(p[k].y);
			if(fx!=fy) fa[fx]=fy,ans--; 
		}i=j+1;
	}
	printf("%d",ans);
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值