uestc summer training #2
A
增广
#include<bits/stdc++.h> using namespace std; const int MAXN = 1000000 + 10; vector<int> g[MAXN]; int a[MAXN], b[MAXN], sz[MAXN], cnt[MAXN]; bool mg[MAXN], vis[MAXN]; int n, m; bool dfs(int u, int f = -1) { if (g[u].empty()) //如果当前数没有位置是成对的(a[i+1]=a[i]+1)当然不可能缝 { return false; } //第一种情况 for (auto &p : g[u]) //枚举当前数每个成对的位置 if (p != f) { if (!vis[p + 1] || cnt[u + 1] == 1) //如果当前这对后面的位置没有被缝并且后面的数数量为1 { mg[p] = vis[p] = vis[p + 1] = 1; //缝起来 返回true if (f != -1) { mg[f] = 0; } return true; } } for (auto &p : g[u]) if (p != f) { if (dfs(u + 1, p + 1)) { mg[p] = vis[p] = vis[p + 1] = 1; if (f != -1) { mg[f] = 0; } return true; } } return false; } int main() { scanf("%d", &n); m = 0; //m是当前数组的数量 for (int i = 0, p(-1); i < n; ++ i) { int x; scanf("%d", &x); if (x == p) //p是上一个加入的数 { sz[m - 1] ++; //如果现在加入的和上一个相同就给上一个sz++ } else //不同的话 加入当前数更新p { p = x; a[m] = x; sz[m ++] = 1; } } n = m; for (int i = 0; i < n; i++) { b[i] = a[i]; } sort(b, b + n); for (int i = m = 0; i < n; i++) //初始化m进行离散化操作 if (i == 0 || b[i] > b[m - 1]) { b[m++] = b[i]; } for (int i = 0; i < n; ++ i) //离散化 { a[i] = lower_bound(b, b + m, a[i]) - b; cnt[a[i]] ++; //计数 计算每种数的数量 } // for (int i = 0; i < n; i++) // { // cout << a[i] << " "; // } // cout << endl; for (int i = 0; i + 1 < n; ++ i) { if (a[i] + 1 == a[i + 1]) //如果后一个是前一个+1就建一条有向边 { g[a[i]].push_back(i); //记录每一对可以缝的位置前面的哪个 } } int ret = n - 1; //答案(缩点之后缝之前的答案 这里claris姐姐写错了 应该是n-1) for (int i = m - 1; i >= 0; -- i) //从大到小尝试是否可以缝当前的数 { if (dfs(i)) //如果返回true即可以缝的话 答案减少一个 { -- ret; //对于每一种数 最多只能缝一次 所以最多-1 } } printf("%d", ret); }
B
签到题
D
模拟题
F
给你一个中序遍历 和每个点的权值 问你存不存在一颗树使得每个节点的祖先和它的权值是互质的
解:
质因数分解+分治模拟
因为中序遍历有个特点 如果一个点是一个子树的根的话 左儿子都在左边 右儿子都在右边
所以要求互质的是一段区间 用GCD来做的话肯定会超时 我们给每个合数一个leftprime表示该合数质因数分解后最小的质数
首先我们从左到右枚举 维护每个质数所到的最右边 更新每个合数的答案 再反过来从右到左做一次
然后模拟树分治找到根递归地看能不能构造树
/*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; typedef unsigned long long ull; const double eps = 1e-8; 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, gakki = 5 + 2 + 1 + 19880611 + 1e9; const int MAXN = 1e6 + 5, MAXM = 1e5 + 5; const int MAXQ = 100010; //int to[MAXM << 1], nxt[MAXM << 1], Head[MAXN], tot = 1; /*inline void addedge(int u, int v, ll c) { to[++tot] = v; nxt[tot] = Head[u]; cost[tot] = c; Head[u] = tot; }*/ inline void read(int &v) { v = 0; char c = 0; int p = 1; while (c < '0' || c > '9') { if (c == '-') { p = -1; } c = getchar(); } while (c >= '0' && c <= '9') { v = (v << 3) + (v << 1) + c - '0'; c = getchar(); } v *= p; } const int N = 10000000 + 5; int MAXX = -1; bool flagsum; int ptot = 0; int num[N]; int father[N]; int prime[N]; bool check[N]; int fanwei[N]; int leftprime[N]; int lans[N], rans[N]; void Euler() { ll now; for (int i = 2; i <= MAXX; i ++) { if (!check[i]) { prime[ptot ++] = i; } for (int j = 0; j < ptot; j ++) { now = 1LL * prime[j] * i; if (now > MAXX) { break; } check[now] = 1; leftprime[now] = prime[j]; if (i % prime[j] == 0) { break; } } } } bool get_ans(int fa, int l, int r) { int flag = 0; if (l > r) { return true; } int aim; int len = (r - l - 1) / 2 + ((r - l - 1) & 1); for (int i = 0; i <= len; i++) { aim = l + i; if (lans[aim] < l && rans[aim] > r) { //cout << fa << " " << l << " " << r << " " << i << endl; //cout << lans[i] << " " << rans[i] << endl; flag = 1; flag = flag && get_ans(aim, l, aim - 1) && get_ans(aim, aim + 1, r); if (flag) { father[aim] = fa; return true; } else { return false; } } aim = r - i; if (lans[aim] < l && rans[aim] > r) { //cout << fa << " " << l << " " << r << " " << i << endl; //cout << lans[i] << " " << rans[i] << endl; flag = 1; flag = flag && get_ans(aim, l, aim - 1) && get_ans(aim, aim + 1, r); if (flag) { father[aim] = fa; return true; } else { return false; } } } return false; } int main() { ios_base::sync_with_stdio(0); cin.tie(0); int n; int cnt; read(n); for (int i = 1; i <= n; i++) { read(num[i]); MAXX = max(MAXX, num[i]); } Euler(); // for (int i = 1; i <= 20; i++) // { // cout << i << " " << leftprime[i] << endl; // } for (int i = 1; i <= n; i++) { rans[i] = n + 1; } for (int i = 1; i <= n; i++) { cnt = num[i]; if (cnt == 1) { continue; } //cout<<i<<" "; if (check[cnt] == 0) { //cout << "l "<<cnt << endl; lans[i] = max(fanwei[cnt], lans[i]); fanwei[cnt] = i; continue; } while (check[cnt]) { //cout << "l " << cnt << endl; int primenow = leftprime[cnt]; lans[i] = max(fanwei[primenow], lans[i]); fanwei[primenow] = i; while (cnt % primenow == 0) { cnt /= primenow; } } if (cnt == 1) { continue; } else { //cout << "l " << cnt << endl; lans[i] = max(fanwei[cnt], lans[i]); fanwei[cnt] = i; } } for (int i = 1; i <= N; i++) { fanwei[i] = n + 1; } for (int i = n; i >= 1; i--) { cnt = num[i]; if (cnt == 1) { continue; } //cout<<i<<" "; if (check[cnt] == 0) { //cout << "r "<<cnt << endl; rans[i] = min(fanwei[cnt], rans[i]); fanwei[cnt] = i; continue; } while (check[cnt]) { //cout << "r "<<cnt << endl; int primenow = leftprime[cnt]; rans[i] = min(fanwei[primenow], rans[i]); fanwei[primenow] = i; while (cnt % primenow == 0) { cnt /= primenow; } } if (cnt == 1) { continue; } else { // cout << "r "<<cnt << endl; // cout << cnt << endl; rans[i] = min(fanwei[cnt], rans[i]); fanwei[cnt] = i; } } // for(int i=1;i<=n;i++) // { // cout<<i<<" "<<lans[i]<<" "<<rans[i]<<endl; // } flagsum = get_ans(0, 1, n); if (!flagsum) { cout << "impossible" << endl; } else { for (int i = 1; i <= n; i++) { cout << father[i]; if (i != n) { cout << " "; } } cout << endl; } return 0; }
G
几何题
H
小的直接暴力 大的加给大的
I
背包DP 记录路径还需要一个二维数组
/*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; typedef pair<int, int> PINT; typedef long long ll; typedef unsigned long long ull; const double eps = 1e-8; 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, gakki = 5 + 2 + 1 + 19880611 + 1e9; const int MAXN = 1e6 + 5, MAXM = 1e5 + 5; const int MAXQ = 100010, INF = 1e9; //int to[MAXM << 1], nxt[MAXM << 1], Head[MAXN], tot = 1; /*inline void addedge(int u, int v, ll c) { to[++tot] = v; nxt[tot] = Head[u]; cost[tot] = c; Head[u] = tot; }*/ inline void read(int &v) { v = 0; char c = 0; int p = 1; while (c < '0' || c > '9') { if (c == '-') { p = -1; } c = getchar(); } while (c >= '0' && c <= '9') { v = (v << 3) + (v << 1) + c - '0'; c = getchar(); } v *= p; } struct node { int first, second, index; } bag[505]; bool cmp(node a, node b) { return (a.first - a.second) > (b.first - b.second); } int dp[505][10005]; int print[505][10005]; PINT before[505][10005]; stack<int> anser; int main() { ios_base::sync_with_stdio(0); cin.tie(0); int n, c; scanf("%d %d", &n, &c); for (int i = 1; i <= n; i++) { scanf("%d %d", &bag[i].first, &bag[i].second); bag[i].index = i; } sort(bag + 1, bag + 1 + n, cmp); int ans = 0; for (int i = 1; i <= n; i++) for (int j = 0; j <= c; j++) { dp[i][j] = -INF; } dp[0][0] = 0; PINT ed = make_pair(0, 0); for (int i = 1; i <= n; i++) { for (int j = 0; j <= c; j++) { if (dp[i - 1][j] > dp[i][j]) { dp[i][j] = dp[i - 1][j]; before[i][j] = make_pair(i - 1, j); print[i][j] = print[i - 1][j]; } } for (int j = 1; j <= c; j++) { if (j >= bag[i].second && j + bag[i].first - bag[i].second <= c) { if (dp[i - 1][j - bag[i].second] != -INF) { if (dp[i][j] < dp[i - 1][j - bag[i].second] + 1) { dp[i][j] = dp[i - 1][j - bag[i].second] + 1; print[i][j] = bag[i].index; before[i][j] = make_pair(i - 1, j - bag[i].second); if (dp[i][j] > ans) { ans = dp[i][j]; ed = make_pair(i, j); } } } } } } int printnow; cout << ans << endl; for (; ed.first; ed = before[ed.first][ed.second]) { if (printnow != print[ed.first][ed.second] && print[ed.first][ed.second] != 0) { anser.push(print[ed.first][ed.second]); } printnow = print[ed.first][ed.second]; } while (!anser.empty()) { cout << anser.top() << " "; anser.pop(); } return 0; }
J
找规律题
我们可以通过打暴力程序对比发现每个2一轮一轮地模拟和一个一个地模拟得到的结果是一样的
但是直接把一个一个滚的模拟交上去会T7 需要再简化一下
每当遇到一个二 找到它左边最近的0和右边最近的0位置分别为L与R
则这一段数列除了L+R-i这个位置变成0 其他位置都会变成1
K
概率题