图论

一些个人认为不错的图论题。

T1:https://www.luogu.org/problemnew/show/P1197

这道题可能一看到没什么思路,但我们可以逆向思维一下,也就是正难则反,把炸毁点想成修建,然后类似于时光倒流那样跑并查集就可以了。

code:

#include<iostream>
#include<cstring>
using namespace std;

const int N=4e5+10, M=2e5+10;
struct edge {
	int v,next;
} e[M<<1];
int n, m, k, ans[N], saven[N], nosave[N];
int cnt, head[N];
int f[N];
bool saveb[N];

void init() {
	memset(saveb, true, sizeof(saveb));
	for (int i=1; i<=n; i++)
		f[i]=i;
	return ;
}

void add(int a, int b) {
	e[++cnt].v=b;
	e[cnt].next=head[a];
	head[a]=cnt;
	return ;
}

int find(int x) {
	if (x==f[x]) return f[x];
	else return f[x]=find(f[x]);
}

void merge(int a, int b) {
	int f1=find(a), f2=find(b);
	if (f1!=f2) f[f2]=f1;
	return ;
}

bool query(int a, int b) {
	int f1=find(a), f2=find(b);
	if (f1!=f2) return false;
	else return true;
}

int main() {
	int x, y, tot=0;
	cin >> n >> m;
	init();
	for (int i=1; i<=m; i++) {
		cin >> x >> y;
		add(x+1, y+1);
		add(y+1, x+1);
	}
	cin >> k;
	for (int i=1; i<=k; i++) {
		cin >> x;
		nosave[i]=x+1;
		saveb[x+1]=false;
	}
	for (int i=1; i<=n; i++)
		if (saveb[i]) saven[++tot]=i;
	int tmp=0;
	for (int i=1; i<=tot; i++) {
		int u=saven[i];
		for (int j=head[u]; j; j=e[j].next) {
			int v=e[j].v;
			if (!query(u, v) && saveb[v]) {
				merge(u, v);
				tmp++;
			}
		}
		
	}
	ans[k]=tot-tmp;
	for (int i=k; i>=1; i--) {
		int u=nosave[i];
		tmp=0;
		for (int j=head[u]; j; j=e[j].next) {
			int v=e[j].v;
			if (saveb[v] && !query(u, v)) {
				merge(u, v);
				tmp++;
			}
		}
		saveb[u]=true;
		ans[i-1]=ans[i]-(tmp-1);
	}
	for (int i=0; i<=k; i++)
		cout << ans[i] << endl;
	return 0;
}

T2:https://www.luogu.org/problemnew/show/P4366#sub

这道题很明显dij是过不去的(SPFA更不用说了),而主要的复杂度就是在于边数太多上,所以我们想怎么优化边数。

这里举例来说:3xor6=5,5的二进制表示为101,可以发现101可以由100和1相加得到,而100是3xor7的值,1则是7xor6的值,即3xor6=3xor7+7xor6,也就是我们可以只考虑3到7的边和7到6的边。那么可以得到这样的一个结论:

对于点i和j,满足ixorj=2^k(k为非负整数),则i和j之间的边我们就需要考虑。变形一下得到j=ixor2^k,k非负整数,j小于等于n,这样总边数就变成了M+nlogn,可以放心的跑dj了。

还有一个需要注意的地方就是编号为0的点也要考虑,因为若中间点大于n的话,可以用零来代替,可以结合上面的例子自行考虑一下。

code:

#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;

typedef pair<int, int> pii;
const int N=1e5+10, M=3e6+10, INF=0x3f3f3f3f;//M要开大点
struct edge {
    int v, w, next;
} e[M];
priority_queue<pii, vector<pii>, greater<pii> > Q;
int n, m, c, sx, sy;
int cnt, head[N];
int dis[N];
bool vis[N];

int read() {
    int x=0, w=1;
    char ch=0;
    while (ch<'0' || ch>'9') {
        if (ch=='-') w=-1;
        ch=getchar();
    } 
    while (ch>='0' && ch<='9') {
        x=(x<<3)+(x<<1)+ch-'0';
        ch=getchar();
    }
    return x*w;
}

void add(int u, int v, int w) {
    e[++cnt].v=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    head[u]=cnt;
    return ;
}

void dijkstra(int s) {
    dis[s]=0;
    Q.push(make_pair(dis[s], s));
    while (!Q.empty()) {
        int u=Q.top().second;
        Q.pop();
        if (vis[u]) continue;
        vis[u]=true;
        for (int i=head[u]; i; i=e[i].next) {
            int v=e[i].v;
            if (dis[v]>dis[u]+e[i].w) {
                dis[v]=dis[u]+e[i].w;
                Q.push(make_pair(dis[v], v));
            }
        }
    }
    return ;
}

int main() {
    memset(dis, INF, sizeof(dis));
    int x, y, z;
    n=read(), m=read(), c=read();
    for (int i=1; i<=m; i++) {
        x=read(), y=read(), z=read();
        add(x, y, z);
    }
    sx=read(), sy=read();
    for (int i=0; i<=n; i++) {
        for (int j=0; j<=25; j++) {
            int to=i^(1<<j);
            if (to<=n) add(i, to, (1<<j)*c);
        }
    }
    dijkstra(sx);
    printf("%d", dis[sy]);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值