图(刷题记录)自用

HDU 5521

难点: 集合内的点之间的关系为完全图,如果按照完全图建立边关系是 O ( n 2 ) O(n^2) O(n2)的,因此不能够直接将集合内的点建立边关系。
做法1: 回想最短路算法,每个点只更新一次(vis数组),在这道题中也是类似,对于一个集合而言,该集合只更新一次。
(每次从队列中取出一个点,然后遍历该点所在的集合,如果该集合还未更新过,则更新该点与该集合中的所有点的最短路距离)
做法2: 集合内的点的边可以转化为 O ( n ) O(n) O(n)的复杂度,对于每个集合 S i S_i Si建立一个虚点 x x x,然后链接 v ∈ S i t o x v \in S_i \quad to \quad x vSitox,边权为 t i t_i ti,然后虚点到每个点之间的边权为 0 0 0 ,这样缩小了边的存储空间,用最短路算法即可

1408E - Avoid Rainbow Cycles

这道题的模型和HDU 5521很类似,故放在一起。
难点类似,集合内的点之间的关系为完全图,且需要根据该图,去使得该图中不存在边颜色各不同的环。
做法:因为集合内的点之间是完全图,因此也不可能把边都建立出来,因此建边同上面类似。然后会发现,一个集合内的点无法构成环,只有多个不同集合内的边会构成环,因此建立边后,采用最大生成树算法即可通过本题(最大生成树保证了新图没环,又保证了删除的节点费用最小)

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int arr[maxn],brr[maxn];
typedef long long ll;
#define fi first
#define se second
#define mp(i,j) make_pair(i,j)
#define plpii pair<ll,pair<int,int> >
int pre[maxn];
inline void init(int n,int m)
{
    for(int i = 0; i <= n + m; ++i) pre[i] = i;
}
int Findpre(int u){
    return pre[u] == u ? u : pre[u] = Findpre(pre[u]);
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int m,n;    cin >> m >> n;
    for(int i = 1; i <= m; ++i)
        cin >>arr[i];
    for(int i = 1; i <= n; ++i)
        cin >>brr[i];
    vector<plpii> vec;
    ll sum = 0;
    for(int i = 1; i <= m; ++i){
        int siz;    cin >> siz;
        for(int j = 0; j < siz; ++j){
            int t;  cin >> t;
            vec.push_back(mp(arr[i] + brr[t],mp(i,t + m)));
            sum += (arr[i] + brr[t]);
        }
    }
    init(n,m);
    sort(vec.begin(),vec.end());
    for(int i = vec.size() -1; i >= 0; --i){
        plpii x = vec[i];
        int u = Findpre(x.se.fi),v = Findpre(x.se.se);
        if(u != v){
            pre[v] = u;
            sum -= x.first;
        }
    }cout << sum << endl;
    return 0;
}

1407E - Egor in the Republic of Dagestan

思考:一开始遇到这个题会发现这个图走的时候,需要判断该点的状态(即为白天/晚上),但是发现这样子状态转移比较困难,但是如果我们进行反向建边,那么更新的终点状态就已经被确定了。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5+5;
const int INF = 0x3f3f3f3f;
struct Node{
    int u,dis,t;
    bool operator <(const Node & rhs)const{
        return dis > rhs.dis;
    }
};
#define fi first
#define se second
#define pii pair<int,int>
#define mp(i,j) make_pair(i,j)
vector<pii> G[maxn];
int dis[maxn][2];
bool vis[maxn][2];
pii pre[maxn][2];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int n,m;    cin >> n >> m;
    for(int i = 0; i < m; ++i){
        int u,v,t;  cin >> u >> v >> t;
    //    G[u].push_back(mp(v,t));
        G[v].push_back(mp(u,t));
    }
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[n][0] = dis[n][1] = 0;
    priority_queue<Node> pq;
    pq.push(Node{n,0,0});
    while(!pq.empty()){
        int u = pq.top().u;
        int t = pq.top().t; pq.pop();
        for(int i = 0; i < G[u].size(); ++i){
            pii v = G[u][i];
            if(dis[v.fi][v.se] > max(dis[u][t],dis[u][t ^ 1]) + 1 ){
                dis[v.fi][v.se] = max(dis[u][t],dis[u][t ^ 1]) + 1;
                pq.push(Node{v.fi,dis[v.fi][v.se],v.se});
                pre[v.fi][v.se] = mp(u,t^1);
                if(dis[u][t] > dis[u][t^1])
                    pre[v.fi][v.se] = mp(u,t);
            }
        }
    }
    int p = 0;
    cout << ( (p = max(dis[1][0],dis[1][1])) == INF ? -1 : p)<<endl;
    pii now = mp(1,1);
    if(dis[1][0] > dis[1][1])  now = mp(1,0);
    int cnt = 0;
    for(int i = 1; i <= n; ++i){
        if(dis[i][0] > dis[i][1]){
            cout << "0";
        }else cout << "1";
    }cout << endl;
 
    return 0;
}

1394B - Boboniu Walks on Graph (哈希集合判断+暴力)

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn = 5e5+5;
const int INF = 0x3f3f3f3f;
const ull base = 233;
const ull base1 = 23;
const ull mod = 1e9 + 7;
typedef long long ll;
#define fi first
#define se second
#define pii pair<int,int>
#define mp(i,j) make_pair(i,j)
ull hash_val[maxn][10];
ull key[maxn];
ull sum_p[10][10];
vector<int> G[maxn];
int ans[maxn];
ull sum = 0;
ll cnt;
int n,m,k;
void check(int n){
    ull sum_ = 0;
    for(int i = 1; i <= k; ++i){
        sum_ += sum_p[i][ans[i]];
    }
    if(sum_ == sum){
        ++cnt;
    }
}
void dfs(int i,int k){
    if(i > k){
        check(n);
        return;
    }
    for(ans[i] = 1; ans[i] <= i; ++ans[i]){
        dfs(i+1,k);
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
 
 
 
    cin >> n >> m >> k;
    for(int i = 0; i < m; ++i){
        int u,v,w;  cin >> u >> v >> w;
        G[u].push_back(v);
    }
    for(int i = 1; i <= n; ++i){
        key[i] = rand() * i * base;
 
        sum += key[i];
    }
 
    for(int i = 1; i <= n; ++i){
        for(int j = 0; j < G[i].size(); ++j){
            hash_val[i][j+1] = key[G[i][j]];
            sum_p[G[i].size()][j+1] += hash_val[i][j+1];
        }
    }
    dfs(1,k);
    cout << cnt << endl;
 
    return 0;
}

G Game On Tree (图上博弈)

#include <bits/stdc++.h>
#define pii pair<int,int>
#define mp(i,j) make_pair(i,j)
using namespace std;
const int maxn = 2e5+5;
vector<int> G[maxn];
int dis[maxn];
int d[maxn];
int ans[maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int n;  cin >> n;
    for(int i = 1; i < n; ++i){
        int u,v;       cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    int k;  cin >> k;   queue<int> q;
    memset(dis,0x3f,sizeof(dis));
    for(int i = 0; i < k; ++i){
        int a;  cin >> a;
        q.push(a);
        dis[a] = 0;
    }
    while(!q.empty()){
        int u = q.front();  q.pop();
        for(int i = 0; i < G[u].size(); ++i){
            if(dis[G[u][i]] > dis[u] + 1){
                dis[G[u][i]] = dis[u] + 1;
                q.push(G[u][i]);
            }
        }
    }

    memset(d,-1,sizeof(d));
    vector<pii> seq;
    for(int i = 1; i <= n; ++i){
        seq.push_back(mp(dis[i],i));
    }sort(seq.begin(),seq.end(),greater<pii>());
    for(auto i : seq){
        int rt = i.second;
        if(d[rt] < dis[rt]){
            priority_queue<pii> pq;
            pq.push(i);
            ans[rt] = max(ans[rt],d[rt] = dis[rt]);
            while(!pq.empty()){
                pii tmp = pq.top();
                int u = pq.top().second;   pq.pop();
                if(d[u] != tmp.first)   continue;
                for(int j = 0; j < G[u].size(); ++j){
                    int v = G[u][j];
                    if(d[u] > 1 && d[v] < d[u] - 1){
                        d[v] = d[u] - 1;
                        ans[v] = max(ans[v],dis[rt]);
                        pq.push(mp(d[v],v));
                    }
                }
            }
        }
    }
    for(int i = 1; i <= n; ++i){
        cout << ans[i] << ' ';
    }cout << endl;

    return 0;
}


P1477 [NOI2008] 假面舞会

(有向图找环 DFS(找出所有环+长度,建边的一个小trick)

P1606 [USACO07FEB]Lilypad Pond G

(最短路建图 + 计数)

P1685 游览

( 拓扑排序+状态转移计算)

P2416 泡芙

(tarjan缩点 + LCA)

P2783 有机化学之神偶尔会做作弊

(tarjan缩点+ LCA)

1484F - Useful Edges

Floyed最短路推导公式(感觉这种多维度的一般需要固定一~两个维度,然后另外两个枚举的做法)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 1e18;
const int maxn = 6e2+5;
ll dis[maxn][maxn];
ll e[maxn][maxn];
struct Edge{
	ll u,v,l;
};
ll lel[maxn];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);	cout.tie(0);
	int n,m;	cin >> n >> m;
	for(int i = 1; i <= n; ++i){
		for(int j = 1; j <= n; ++j)	dis[i][j] = inf;
		dis[i][i] = 0;
	}
	for(int i = 1; i <= m; ++i){
		ll u,v,w;	cin >> u >> v >> w;
		e[u][v] = e[v][u] = w;
		dis[u][v] = dis[v][u] = min(dis[u][v],w);
	}
	for(int k = 1; k <= n; ++k){
		for(int i = 1; i <= n; ++i){
			for(int j = 1; j <= n; ++j){
				dis[i][j] = min(dis[i][j],dis[i][k] + dis[k][j]);
			}
		}
	}
	int q;	cin >> q;
	vector<Edge> vec;
	for(int i = 0; i < q; ++i){
		ll u,v,l;	cin >> u >> v >> l;
		vec.push_back(Edge{u,v,l});
		vec.push_back(Edge{v,u,l});
	}


	int ans = 0;

	for(int i = 1; i <= n; ++i){
		for(int j = 1; j <= n; ++j){
			lel[j] = -inf;
		}
		for(int j = 0; j < vec.size(); ++j){
			ll u = vec[j].u,l = vec[j].l,v = vec[j].v;
			lel[u] = max(lel[u], l - dis[i][v]);
		}
		for(int j = i + 1; j <= n; ++j){
			if(e[i][j] == 0)	continue;
			for(int k = 1;k <= n; ++k){
				//cout <<"k: " <<lel[k] << endl;
				if(lel[k] >= dis[k][j] + e[i][j]){
					++ans;
					break;
				}
			}
		}

	}

	cout << ans << endl;

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值