文章目录
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
v∈Sitox,边权为
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;
}