Codeforces 987 K预处理BFS 3n,7n+1随机结论题/不动点逆序对 X&Y=0连边DFS求连通块数目
A
/*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; const int dir[8][2] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}, {1, 1}, {1, -1}, { -1, -1}, { -1, 1}}; const int mod = 1e9 + 7; const int gakki = 5 + 2 + 1 + 19880611 + 1e9; const int N = 55 + 5; int n, K; ll sum[N][N]; ll a[N]; bool dp[N][N]; map<string, int> mp2; map<int, string> mp; int visit[10]; int main() { ios_base::sync_with_stdio(0); cin.tie(0); mp2["purple"] = 1; mp2["green"] = 2; mp2["blue"] = 3; mp2["orange"] = 4; mp2["red"] = 5; mp2["yellow"] = 6; mp[1] = "Power"; mp[2] = "Time"; mp[3] = "Space"; mp[4] = "Soul"; mp[5] = "Reality"; mp[6] = "Mind"; int n; cin >> n; for (int i = 1; i <= n; i++) { string now; cin >> now; visit[mp2[now]]++; } cout << 6 - n << endl; for (int i = 1; i <= 6; i++) { if (!visit[i]) { cout << mp[i] << endl; } } return 0; }
B
/*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; const int dir[8][2] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}, {1, 1}, {1, -1}, { -1, -1}, { -1, 1}}; const int mod = 1e9 + 7; const int gakki = 5 + 2 + 1 + 19880611 + 1e9; const int N = 55 + 5; int main() { ios_base::sync_with_stdio(0); cin.tie(0); ll x, y; cin >> x >> y; if (x == y || x == 2 && y == 4 || x == 4 && y == 2) { cout << "=" << endl; return 0; } if (x == 1 || y == 1) { if (x == 1) { cout << "<" << endl; } else { cout << ">" << endl; } return 0; } if (max(x, y) <= 10) { ll ansx = 1; for (int i = 1; i <= y; i++) { ansx *= x; } ll ansy = 1; for (int i = 1; i <= x; i++) { ansy *= y; } if (ansx > ansy) { cout << ">" << endl; } else { cout << "<" << endl; } return 0; } if (x > y) { cout << "<" << endl; } else { cout << ">" << endl; } return 0; }
C
/*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; const int dir[8][2] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}, {1, 1}, {1, -1}, { -1, -1}, { -1, 1}}; const int mod = 1e9 + 7; const int gakki = 5 + 2 + 1 + 19880611 + 1e9; const int N = 55 + 5; int num[3005]; ll cost[3005]; ll dp[3005][4]; int main() { ios_base::sync_with_stdio(0); cin.tie(0); int n; cin >> n; for (int i = 1; i <= n; i++) { cin >> num[i]; } for (int i = 1; i <= 3000; i++) { for (int j = 1; j <= 3; j++) { dp[i][j] = LLONG_MAX; } } for (int i = 1; i <= n; i++) { cin >> cost[i]; dp[i][1] = cost[i]; } for (int i = 2; i <= n; i++) { for (int j = i - 1; j >= 1; j--) { for (int k = 2; k <= 3; k++) { if (dp[j][k - 1] != LLONG_MAX&&num[i]>num[j]) { dp[i][k] = min(cost[i] + dp[j][k - 1], dp[i][k]); } } } } ll anser = LLONG_MAX; for (int i = 1; i <= n; i++) { anser = min(anser, dp[i][3]); } if (anser == LLONG_MAX) { cout << -1 << endl; } else { cout << anser << endl; } return 0; }
D. Fair
题意:
给你N个点M条边的连通图(100000) 和K个特殊地方(100) S个目标地(100)
每个特殊的地方有某种特产 问你从1到N每个点要拿S个特产的最少的花费
解:
给每个特产都建一个超级点 然后BFS 处理出1到N每个点到每种特产的最小距离
然后贪心取前S个即可 复杂度nklogk
/*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; const int dir[8][2] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}, {1, 1}, {1, -1}, { -1, -1}, { -1, 1}}; const int mod = 1e9 + 7; const int gakki = 5 + 2 + 1 + 19880611 + 1e9; const int N = 1e5 + 105; int p[N]; vector<int> g[N]; int ans[N]; int dist[N]; int que[N]; int dp[N][105]; int getl, getr; int main() { ios_base::sync_with_stdio(0); cin.tie(0); int n, m, k, s; cin >> n >> m >> k >> s; for (int i = 1; i <= n; i++) { cin >> p[i]; } int u, v; for (int i = 1; i <= m; i++) { cin >> u >> v; g[u].pb(v); g[v].pb(u); } for (int i = 1; i <= k; i++) { getl = 1; getr = 0; mem(dist, 0); for (int j = 1; j <= n; j++) { if (p[j] == i) { dist[j] = 1; que[++getr] = j; } } while (getl <= getr) { int len = g[que[getl]].size(); for (int w = 0; w < len; w++) { int to = g[que[getl]][w]; if (!dist[to]) { dist[to] = dist[que[getl]] + 1; que[++getr] = to; } } getl++; } for (int j = 1; j <= n; j++) { dp[j][i] = dist[j] - 1; } } for (int i = 1; i <= n; i++) { sort(dp[i] + 1, dp[i] + 1 + k); } for (int i = 1; i <= n; i++) { for (int j = 1; j <= s; j++) { ans[i] += dp[i][j]; } } for (int i = 1; i <= n; i++) { cout << ans[i] << " "; } cout << endl; return 0; }
E. Petr and Permutations
题意:
给你1到N的一种排列 A会选3n次每次随机一对位置交换 B会选7n+1次
问你给你的数列是A得到的可能性大还是B的可能性大
①:
可以猜到结论 观察可以得知3n和7n+1的奇偶性永远是不同的
所以我们计算原数列所需要的最小交换次数 当这个次数与A奇偶性相同就是A 反之则是B
(因为要与原数列相同的话在最小交换次数基础上要加偶数次)
/*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; const int dir[8][2] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}, {1, 1}, {1, -1}, { -1, -1}, { -1, 1}}; const int mod = 1e9 + 7; const int gakki = 5 + 2 + 1 + 19880611 + 1e9; const int N = 1e6 + 105; int par[N], hs[N]; int sz[N]; int num[N]; int cnt; void init(int n) { for (int i = 0; i <= n; i++) { par[i] = i, hs[i] = i, sz[i] = 1; } } int find(int x) { return par[x] == x ? x : par[x] = find(par[x]); } void unite(int x, int y) { x = find(x); y = find(y); if (x != y) { par[x] = y, sz[y] += sz[x]; } } int visit[N]; int main() { ios_base::sync_with_stdio(0); cin.tie(0); int n; cin >> n; init(n); for (int i = 1; i <= n; i++) { cin >> num[i]; } for (int i = 1; i <= n; i++) { if (num[i] != i) { unite(i, num[i]); } } for (int i = 1; i <= n; i++) { par[i] = find(i); } ll anser = 0; for (int i = 1; i <= n; i++) { if (!visit[par[i]]) { anser += sz[par[i]] - 1; visit[par[i]] = 1; } } //cout << anser << endl; if (anser % 2 == 3 * n % 2) { cout << "Petr" << endl; } else { cout << "Um_nik" << endl; } return 0; }
②:
也可以用不动点+逆序对分情况处理
当n不小于20000时 考察不动点的数量 n小于20000时取逆序对的数量判断奇偶性
int main() { int i,j,a1,a2; _(n); fo(i,n) _(a[i]); fo(i,n) an+=(a[i]==i); if(n>=20000) puts(an>=20?"Petr":"Um_nik"); else { fo(i,n) fo1(j,i+1,n) if(a[j]<a[i]) ans++; puts((ans+n)%2?"Um_nik":"Petr"); } }
F. AND Graph
题意:
给你0 - 2n-1范围中的m个数 (n<=22)
如果两个数字Ai Aj 两个&的值为0 则这两个点之间有一条边 问你最后的连通块有几个
解:
暴力题.. 假设有X,Y两个数 X的二进制恰好是Y的基础上多出一位1 比如 101 与 001 我们称X为Y的祖先 Y是X的子孙
这样能与祖先X相连的点 肯定能和Y相连 这样X,Y其实就通过对立数间接相连
所以我们每次DFS一个数的时候 把X全部子孙找出来 也把所有对立数(包括子孙的)找出来 他们肯定就是一个连通块
因为N最大是22 所以我们可以直接暴力循环枚举 开两个bool 数组 num[i]为true表示i是被选中的数 而vis[i]为true表示i被DFS过了
/*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; const int dir[8][2] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}, {1, 1}, {1, -1}, { -1, -1}, { -1, 1}}; const int mod = 1e9 + 7; const int gakki = 5 + 2 + 1 + 19880611 + 1e9; const int N = (1 << 22) + 5; int n, m, now; int anser; bool num[N], vis[N]; void dfs(int x) { if (vis[x]) //如果之前已经DFS到过 { return ; } vis[x] = true; for (int i = 0; i < n; i++) if (x & (1 << i)) //DFS每个二进制比X少一位1的数保证main循环中不会被循环到 { dfs(x ^ (1 << i)); } if (num[x]) //如果X是存在的 找所有X对立面的数 { dfs((1 << n) - 1 - x); } } int main() { cin >> n >> m; for (int i = 1; i <= m; i++) { scanf("%d", &now); num[now] = true; } for (int i = (1 << n) - 1; i >= 0; i--) if (!vis[i] && num[i]) //如果有存在的数且没有被DFS到 { anser++; //连通块答案数+1 dfs((1 << n) - 1 - i); //DFS全部与i连通的数 } cout << anser << endl; return 0; }