总体:
7.3
最短路训练:熟悉Dijkstra,floyed,spfa,最长路,差分约束,负环判断(spfa)
7.4
拓扑排序(强化),欧拉路径,裴蜀定理,最小生成树,
7.5
st表,(基础)线段树,树状数组回顾,最小生成树训练
以下为遇到的感觉挺好的题
拓扑排序(强化):在不破坏结点的先后顺序的情况下,把DAG拉成一条链
Kahn算法:不断删入度为0的点,返回值为是否存在环,若题目需要的字典序最小方案,将queue改为priority_queue(优先队列即可)
(CF510C Fox And Names)
题意:给定一串名字,改变26个字母表的顺序,使得名字按字母表的顺序排序
思路:因为拓扑排序是不改变结点的先后顺序情况下把DAG拉成一条链,所以可以比较相邻的两个名字,找到第一个不同处,
连边,然后跑拓扑排序就可以了,如果存在环,则impossible,巨坑的一个点,调了好久啊,子序列必须在前面
#include<iostream>
#include<queue>
#include<math.h>
#define ms(x,y) memset(x,y,sizeof x);
#define YES cout<<"YES"<<'\n';
#define NO cout<<"NO"<<'\n';
#define fr(i,z,n) for(int i = z;i <= n; i++)
#define ufr(i,n,z) for(int i = n;i >= z; i--)
typedef long long ll;
const ll maxn = 2e5 + 10, inf = 1e18;
const ll mod = 1e9 + 7;
int i, j;
using namespace std;
string a[maxn];
char A[maxn];
struct edge {
int v, w, next;
}e[maxn];
int cnt = 0; int head[maxn];
int in[maxn];
bool vis[maxn];
void insert(int u, int v, int w) {
cnt++;
e[cnt].v = v;
e[cnt].next = head[u];
head[u] = cnt;
}
bool tuopu(int n) { //拓扑排序
int cnt1 = 0;
queue<int>q;
for (int i = 1; i <= n; i++) {
if (!in[i]) {
q.push(i);
vis[i] = 1;
A[++cnt1] = i + 'a' - 1;
//cout << A[cnt1] << ' ';
}
}
//cout << '\n';
while (!q.empty()) {
int now = q.front();
q.pop();
if (!vis[now]) {
A[++cnt1] = now + 'a' - 1;
vis[now] = 1;
}
for (int i = head[now]; i; i = e[i].next) {
int v = e[i].v;
in[v]--;
if (!in[v]) {
q.push(v);
//cout << A[cnt1] << ' ';
}
}
}
//cout << cnt1 << '\n';
return n == cnt1;
}
void solve() {
int n;
cin >> n;
fr(i, 1, n) {
cin >> a[i];
}
fr(i, 2, n) {
int flag = -1;
fr(j, 0, min(a[i].size()-1, a[i - 1].size()-1)) {
if (a[i][j] != a[i - 1][j]) {
flag = j;
break;
}
}
if (flag != -1) {
insert(a[i-1][flag] - 'a' + 1, a[i][flag] - 'a' + 1, 1);
in[a[i][flag] - 'a' + 1]++;
}
if (flag==-1 && a[i - 1] != a[i] && a[i].length() < a[i - 1].length())
{
cout << "Impossible" << endl;
return ;
}
}
if (tuopu(26)) {
for (int i = 1; i <= 26; i++) {
cout << A[i];
}
}
else {
cout << "Impossible" << '\n';
}
}
signed main()
{
ios::sync_with_stdio(false);
int t = 1;
while (t--) {
solve();
}
}
P1396 营救
题意:给定n个区,m条道路,起点s,终点t,使s到t的最大拥挤度最小,求最大拥挤度
思路:因为是使得最大拥挤度最小,可以将spfa求最短路的比较策略更改,
变成比较终点的距离与max(起点,边权)因为起点要到终点必须经过边权,所以必须取max,终点的距离<max则更新
这道题二分+bfs也可以做,因为关键字最大的最小太明显了
总结:一般都是求最短路,或是求路径上最大值最小,这道题可以作为(求路径上最大值最小模板题)
#include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<string.h>
#define ms(x,y) memset(x,y,sizeof x);
#define YES cout<<"YES"<<'\n';
#define NO cout<<"NO"<<'\n';
#define fr(i,z,n) for(int i = z;i <= n; i++)
#define ufr(i,n,z) for(int i = n;i >= z; i--)
typedef long long ll;
const ll maxn = 2e6 + 10, inf = 1e18;
const ll mod = 1100003;
using namespace std;
int n, m,s,t;
int head[maxn];
int size1;
bool vis[maxn];
int dis[maxn];
int in[maxn];
struct edge {
int v, w, next;
edge() {};
edge(int _v, int _w, int _next) {
v = _v;
w = _w;
next = _next;
}
}e[maxn * 2];
void init() {
memset(head, -1, sizeof(head));
size1 = 0;;
}
void insert(int u, int v, int w) {
e[size1] = edge(v, w, head[u]);
head[u] = size1++;
}
bool spfa(int u) { //spfa板子
memset(vis, false, sizeof(vis));
vis[u] = true;
memset(dis, 0x3f, sizeof(dis));
//memset(dis, -0x3f, sizeof(dis));
dis[u] = 0;
memset(in, 0, sizeof(in));
queue<int>q;
q.push(u);
while (!q.empty()) {
u = q.front();
q.pop();
vis[u] = false;
for (int j = head[u]; ~j; j = e[j].next) {
int v = e[j].v;
int w = e[j].w;
if (dis[v] > max(dis[u] , w)) {
//if (dis[v] < dis[u] + w) {
dis[v] = max(dis[u] , w);
if (!vis[v]) {
q.push(v);
vis[v] = true;
++in[v];
if (in[v] > n) {
//cout << in[v] << '\n';
return true; //有负环
}
}
}
}
}
return false;
}
void solve() {
init();
cin >> n >> m>>s>>t;
fr(i, 1, m) {
int a, b, c;
cin >> a >> b >> c;
insert(a, b, c);
insert(b, a, c);
}
spfa(s);
cout << dis[t] << '\n';
}
signed main()
{
ios::sync_with_stdio(false);
int t = 1;
//cin>>t;
while (t--) {
solve();
}
}
P4549 【模板】裴蜀定理
题意:给定n个整数A,求另一个整数序列X,使得S=Ai*Xi(i从1到n)S>0并且S尽可能小,求S
裴蜀定理:ax+by=c(x,y为正整数且gcd(a,b)|c),将其两个变量推广到多个变量,S尽可能小,
因为每次求的是满足条件的最小情况,所以满足
总结:这道题虽然只是个模板题,但感觉定理挺叼的,记录一下
#include<bits/stdc++.h>
#define ms(x,y) memset(x,y,sizeof x);
#define YES cout<<"YES"<<'\n';
#define NO cout<<"NO"<<'\n';
#define fr(i,z,n) for(int i = z;i <= n; i++)
#define ufr(i,n,z) for(int i = n;i >= z; i--)
typedef long long ll;
const ll maxn = 500, inf = 1e18;
const ll mod = 1100003;
using namespace std;
stack<char>sta;
vector<vector<int>>v(maxn, vector<int>(maxn));
int a[maxn]; //记录出入度
int cnt[2];
int m;
int n;
int gcd(int a, int b) { //辗转相除法求最大公约数
return !b ? a : gcd(b, a % b);
}
void solve() {
cin >> n;
int ans = 0;
fr(i, 1, n) {
int x;
cin >> x;
ans = gcd(abs(x), ans);
}
cout << ans << '\n';
}
signed main()
{
ios::sync_with_stdio(false);
int t = 1;
//cin >> t;
while (t--) {
solve();
}
}
Shichikuji and Power Grid
题意:给定n个城市,一个城市有电,必须在这个城市有发电站或者和一个有电的城市用电缆相连
在一个城市建造发电站的代价是c[i],i和j两个城市相连的代价是k[i] + k[j] 乘上两者的曼哈顿距离
求最小代价,建发电站个数及具体城市,连线个数及具体连线
思路:因为可以有建发电站这个选择,所以有个很好的思路就是,建一个源点0到各点边,边权为c[i],
这样跑一遍最小生成树即可,这里建源点到各点边借鉴了差分约束里的思想
总结:这题的构造很巧妙,可以记录一下
#include<iostream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<cstring>
#include<math.h>
#include<map>
#include<vector>
#include<stack>
#define ms(x,y) memset(x,y,sizeof x);
#define YES cout<<"YES"<<'\n';
#define NO cout<<"NO"<<'\n';
#define fr(i,z,n) for(int i = z;i <= n; i++)
#define ufr(i,n,z) for(int i = n;i >= z; i--)
#define int long long
typedef long long ll;
const ll maxn = 2e3+10, inf = 1e18;
const ll mod = 1e9 + 7;
using namespace std;
struct edge {
int x, y, w;
inline int operator < (const edge& p) const {
return w < p.w;
}
}e[maxn*maxn];
int x[maxn], y[maxn], c[maxn];
int k[maxn];
int f[maxn];
int d[maxn][2];
//bool cmp(edge a, edge b) {
// return a.w < b.w;
//}
int find(int x) {
return (x == f[x]) ? x : find(f[x]);
}
void solve() {
int n;
cin >> n;
int cnt = 0;
ll ans = 0;
fr(i, 1, n) {
cin>>x[i];
cin>>y[i];
}
fr(i, 1, n) {
int w;
cin>> w;
e[++cnt].x = 0;
e[cnt].y = i;
e[cnt].w = w;
}
fr(i, 1, n) {
cin>>k[i];
}
fr(i, 1, n) {
fr(j, i + 1, n) {
e[++cnt].x = i;
e[cnt].y = j;
e[cnt].w = (k[i] + k[j]) * (abs(x[i] - x[j]) + abs(y[i] - y[j]));
}
}
sort(e + 1, e + 1 + cnt);
fr(i, 1, n) {
f[i] = i;
}
int num=0;
int num1 = 0;
for (int i = 1; i <= cnt; i++) {
int x = find(e[i].x);
int y = find(e[i].y);
if (x == y)
continue;
f[min(x, y)] = max(x, y);
ans += e[i].w;
//cout << ans << '\n';
if (!e[i].x) {
c[++num] = e[i].y;
}
else {
d[++num1][0] = e[i].x;
d[num1][1] = e[i].y;
}
}
cout << ans << '\n';
cout << num << '\n';
fr(i, 1, num) {
cout << c[i] << ' ';
}
cout << '\n';
cout << num1 << '\n';
fr(i, 1, num1) {
cout << d[i][0] << ' ' << d[i][1] << '\n';
}
}
signed main() {
ios::sync_with_stdio(false);
int t = 1;
//cin >> t;
while (t--) {
solve();
}
return 0;
}