codeforces round 919 dv2 C Partitioning the Array
大致题意,对于n约数i,我们把原数组分成份,并且每份中有i个元素,对于每个分组情况,如果存在一个数m使得数组中元素modm后使得每个部分的数组完全相同,如果存在那么ans ++求ans
这是一道数论题,数论题的关键在于将数学问题用数学公式表示出来,现在考虑如果存在一个数组是否存在一个整数m使得数组amodm后所有元素相同
对于这个简化的问题首先列出数学公式 对于, 有
对这个同余式子移项后可得到,对于这个式子我们得到这样一个结论是可以整除的。当然m不能为1,现在把这个式子拓展到整个数组,那么就有了所有相邻元素相减的绝对值都应该整除m,并且m不为1,换句话说这些数两两不互质。当然m的最大值就是这些数的gcd了(codeforces round991 div3 F Maximum modulo equality)。
现在再回到这个问题上,这个问题就相当于这个m必须同时满足很多个数组(个,每个子数组中的元素)每个数组求出gcd中,再将这些gcd求总共gcd,只要gcd不为1,就说明ans可以++。当然这里也涉及到时间复杂度的计算,首先的约数预处理最外层的for是约数个数,数据范围内最大的约数个数为168个,总之约数的个数都很小。紧接着的两重循环不大会算,但是感觉可以过:(
#include <bits/stdc++.h>
using namespace std;typedef long long ll;
const int N = 2e5 + 10;int gcd(int a, int b) {return b? gcd(b, a % b): a;
}void solve() {int n;cin >> n;vector<int> a(n + 1);for(int i = 1; i <= n; i ++ ) cin >> a[i];vector<int> divi;for(int i = 1; i <= n / i; i ++ ) {if(n % i == 0) {divi.push_back(i);if(n / i != i) divi.push_back(n / i);}}int ans = 1;for(auto t: divi) {//168bool ok = true;if(t == n) continue;else {vector<int> alls;for(int i = 1; i <= t; i ++ ) {int _gcd = -1;for(int j = i + t; j <= n; j += i ) {if(_gcd == -1) _gcd = abs(a[j] - a[j - t]);else _gcd = gcd(_gcd, abs(a[j] - a[j - t]));}alls.push_back(_gcd);}int _gcd = -1;for(auto t: alls) {if(_gcd == -1) _gcd = t;else _gcd = gcd(t, _gcd);}if(_gcd == 1) ok = false;}if(ok) ans ++;}cout << ans << "\n";
}int main() {ll t;cin >> t;while(t -- ) solve();return 0;
}
codeforces round914div2 C array game
这道题是个tips题,注意到当k大于3时答案总是0,在遇到无从下手的题目时一定要试着去寻找一些特判情况,将答案简化
#include <bits/stdc++.h>
using namespace std;typedef long long ll;
const ll INF = 1e18;void solve() {ll n, k;cin >> n >> k;vector<ll> a(n + 1);ll minv = INF;for(int i = 1; i <= n; i ++ ) {cin >> a[i];minv = min(minv, a[i]);}sort(a.begin() + 1, a.end());ll ans = INF;if(k > 2) {cout << "0\n";return;} else if(k == 1) {for(int i = 1; i <= n; i ++ ) {for(int j = 1; j <= n; j ++ ) {if(i == j) continue;ans = min(ans, abs(a[i] - a[j]));}}} else if(k == 2) {for(int i = 1; i <= n; i ++ ) {for(int j = i + 1; j <= n; j ++ ) {if(i == j) continue;ll del = a[j] - a[i];int l = 1, r = n;while(l < r) {int mid = l + r >> 1;if(a[mid] >= del) r = mid;//找到第一个大于等于del的数else l = mid + 1;}ans = min(ans, abs(a[l] - del));ans = min(ans, abs(a[l - 1] - del));}}}cout << min(ans, minv) << "\n";
}int main() {ll t;cin >> t;while(t -- ) solve();return 0;
}
D set to max
题目大意:给出两个数组,a,b,每次可以从a中任意选择一段区间,并且把区间中元素赋值成区间最大值,easy version和hard version的区别就在于数据范围,有经验的话会立刻明白这个数据范围的作用就是限制求区间最值时的方式,时间复杂度允许的话可以直接暴力求解,反之则可以用线段树维护。现在去思考如何解决这个问题:为了方便我们对数据进行处理,把目标区间取出来是必要的。现在考虑方案的可行性,下述区间的表述都是取出来的区间段,即对于一个区间段的任意,满足。如果a中该区间段最大值大于了该区间中b的值(设为),那么直接判负,因为较大数值会覆盖较小的数值.。同样的如果想要赋值成x,就要保证a数组中存在x,并且在达到x之前没有比x更大的数。这样就结束了吗,上文中提到了,较大值会覆盖最小值,倘若在枚举到当前区间前维护的区间的x比该区间的x要大,并且恰好我这个区间所需要的值被覆盖掉了,那么会导致可行的方案被判否,因此要贪心的以x值较小的区间开始维护,并且在以后的维护中,如果在a中的x必须跨越先前已经维护过的区间,那么答案就会被判否(因为会将以前维护好的覆盖掉)
现在思路就明朗了,时间复杂度整体可控
代码:
#include <bits/stdc++.h>
using namespace std;typedef long long ll;
const int INF = 0x3f3f3f3f;void solve() {int n;cin >> n;vector<int> a(n + 2), b(n + 1);for(int i = 1; i <= n; i ++ ) cin >> a[i];for(int i = 1; i <= n; i ++ ) cin >> b[i];map<int, int> ma;a[0] = a[n + 1] = INF;vector<int> L(n + 1), R(n + 1);for(int i = 1; i <= n; i ++ ) {if(ma[b[i]] == 0) L[i] = 0;else L[i] = ma[b[i]];ma[a[i]] = i;}ma.clear();for(int i = n; i >= 1; i -- ) {if(ma[b[i]] == 0) R[i] = n + 1;else R[i] = ma[b[i]];ma[a[i]] = i;}struct node {int l, r, val, tag;bool operator < (const node &W) {return val < W.val;}};vector<node> tr(n * 4 + 1);auto pushup = [&](int u) {tr[u].val = max(tr[u << 1].val, tr[u << 1 | 1].val);};function<void(int, int, int)> build = [&](int u, int l, int r) {tr[u].l = l, tr[u].r = r;if(l == r) return;int mid = l + r >> 1;build(u << 1, l, mid);build(u << 1 | 1, mid + 1, r);};function<void(int, int)> insert = [&](int u, int pos) {if(tr[u].l == tr[u].r && tr[u].l == pos) tr[u].val = a[pos];else {int mid = tr[u].l + tr[u].r >> 1;if(mid >= pos) insert(u << 1, pos);if(mid < pos) insert(u << 1 | 1, pos);pushup(u);}};function<int(int, int, int)> query = [&](int u, int l, int r) {if(tr[u].l >= l && tr[u].r <= r) return tr[u].val;else {int res = 0;int mid = tr[u].l + tr[u].r >> 1;if(mid >= l) res = query(u << 1, l, r);if(mid < r) res = max(res, query(u << 1 | 1, l, r));return res;}};build(1, 1, n);for(int i = 1; i <= n; i ++ ) insert(1, i);vector<node> st(4 * n + 1);function<void(int, int, int)> build1 = [&](int u, int l, int r) {st[u].l = l, st[u].r = r;if(l == r) return;int mid = l + r >> 1;build1(u << 1, l, mid);build1(u << 1 | 1, mid + 1, r);};auto pushup1 = [&](int u) {st[u].val = st[u << 1].val | st[u << 1 | 1].val; };auto pushdown = [&](int u) {st[u << 1].val |= st[u].tag;st[u << 1].tag |= st[u].tag;st[u << 1 | 1].val |= st[u].tag;st[u << 1 | 1].tag |= st[u].tag;st[u].tag = 0;};function<void(int, int, int)> insert1 = [&](int u, int l, int r) {if(st[u].l >= l && st[u].r <= r) {st[u].val = 1;st[u].tag = 1;} else {pushdown(u);int mid = st[u].l + st[u].r >> 1;if(mid >= l) insert1(u << 1, l, r);if(mid < r) insert1(u << 1 | 1, l, r);pushup1(u);}};function<int(int, int, int)> query1 = [&](int u, int l, int r) {if(st[u].l >= l && st[u].r <= r) return st[u].val;else {int res = 0;int mid = st[u].l + st[u].r >> 1;if(mid >= l) res = query1(u << 1, l, r);if(mid < r) res |= query1(u << 1 | 1, l, r);return res;}};build1(1, 1, n);vector<node> pos;int i = 1, j = 1;while(i <= n && j <= n) {while(j <= n && b[i] == b[j]) j ++;pos.push_back({i, j - 1, b[i]});i = j;}//cout << L[4] << " ";sort(pos.begin(), pos.end());for(int k = 0; k < pos.size(); k ++ ) {auto t = pos[k];int l = t.l, r = t.r, x = t.val;//cout << l << " " << r << " " << x << "\n";int max_val = query(1, l, r);if(max_val > x) {cout << "NO\n";return;}if(max_val == x) {insert1(1, l, r);continue;}bool ok1 = false;bool ok2 = false;if(a[L[l]] == x && !query1(1, L[l], l) && (query(1, L[l], l) == x)) ok1 = true;if(a[R[r]] == x && !query1(1, r, R[r]) && (query(1, r, R[r]) == x)) ok2 = true;if(!ok1 && !ok2) {cout << "NO\n";return;} else {/*for(int i = l; i <= r; i ++ ) insert1(1, i);*/insert1(1, l, r);}}//cout << query1(1, 1, 2) << " ";cout << "YES\n";
}int main() {int t;cin >> t;while(t -- ) solve();return 0;
}
codeforces round 932 div2 C find a mine
这是一道交互题,这道题这道题启示我到一个点的曼哈顿距离相等的所有点的路径是个菱形(如果没有超过图的边界的话)
D1. XOR Break — Solo Version
题目大意:对于x我们可以将x设为y或者xXORy并且满足并且问是否可以将x变成m
数据范围很大,并且有xor操作,直接考虑拆位
现在似乎仍然无从下手,那么试着挖掘一些隐含在题目中的性质
m小于x试着考虑一下这个条件的性质:
容易得到,如果xy满足小于关系那么有
x: same 1 ??????
m:same 0 ??????
从高位起第一位不同的二进制表示,x为1,m为0
再考虑如果x Xor y < x要满足什么条件
x: same 1 ??????
y : 00000 1 ??????
m : same 0 ??????(x xor y = m)
其实这里可以注意到如果same存在1的话,那么后面的????填什么都可以因为y肯定比x小并且m本来就比x小,现在要讨论的情况就是same全为0的情况。如果m的?号位为0的话,那么x和y的?号位要相同,不是0就是1,反之?号位不同,也就是说,如果在same 1以后的二进制表示位中,如果m中出现的第一个1对应的x中二进制中表示位是1的话,那么我们同样可以将x转化为m,反之就证明当前的y比x要小,不满足题意,这时候我们要考虑是否可以借助一个中间值转化一下,使得这一位的值变成1(借位),借位只能向1借,并且不能same后面的那位1实际上是不能动的,这里可以手动模拟一下,也就是说,如果在same1后面的二进制位到该位之间x二进制表示位为1的话,我们就可以借助这个中间值将其转化为上述的情况。(可以找个较小的数值模拟一下)
代码:
#include <bits/stdc++.h>
using namespace std;typedef long long ll;void solve() {ll n, m;cin >> n >> m;bool ok = 0;ll pos = -1;for(ll i = 62; i >= 0; i -- ) {if(n >> i & 1) {if(!(m >> i & 1)) {pos = i;break;}}if(n >> i & 1) ok = true;}//cout << pos << "LL";if(ok) {cout << "1\n";cout << n << " " << m << "\n";} else {bool flag = false;for(ll i = pos - 1; i >= 0; i -- ) {if(m >> i & 1) {if(n >> i & 1) {flag = true;break;}if(!(n >> i & 1)) break;}}if(flag) {cout << "1\n";cout << n << " " << m << "\n";} else {/*0 0 0 0 0 1 a b c 00 0 0 0 0 1 a b c 10 0 0 0 0 0 0 0 0 1*/ll pos1 = -1;for(ll i = pos - 1; i >= 0; i -- ) {if(m >> i & 1) break;if(n >> i & 1) {pos1 = i;break;}}if(pos1 == -1) cout << "-1\n";else {cout << "2\n";ll res = n;ll now = 1ll << pos;for(ll i = 62; i >= pos; i -- ) {res -= (n >> i & 1) * (1ll << i);}for(ll i = pos1 - 1; i >= 0; i -- ) {if((m >> i & 1) && !(n >> i & 1)) {res += 1ll << i;break;}}cout << n << " " << res << " " << m << "\n";}}}
}int main() {ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);ll t;cin >> t;while(t -- ) solve();return 0;
}
codeforces round 991 div3 G
题目大意,从一颗树中删除一条链,找到删除后联通块数量的最大值
树类的问题,直接考虑dfs,树形dp
首先有一个很重要的性质,路径一定要尽可能多的删(除了叶子节点),并且每个点的贡献与这个点的临边数量有关,其实思考一下就是树的直径,只不过把边权换成了点权...
这里说一下树的直径:
代码:
#include <bits/stdc++.h>
using namespace std;typedef long long ll;void solve() {int n;cin >> n;vector<vector<int>> adj(n + 1);for(int i = 2; i <= n; i ++ ) {int u, v;cin >> u >> v;adj[u].push_back(v);adj[v].push_back(u);}vector<int> dp(n + 1);int ans = 0;function<void(int, int)> dfs = [&](int u, int fa) {int tot = adj[u].size();dp[u] = max(0, tot - 2);int fir = -1, sec = -1;bool flag = true;for(auto t: adj[u]) {if(t == fa) continue;flag = false;dfs(t, u);dp[u] = max(dp[u], tot - 2 + dp[t]);if(dp[t] >= fir) {sec = fir;fir = dp[t];} else if(dp[t] >= sec) {sec = dp[t];}int res = max(0, fir) + max(0, sec);if(tot == 1) res ++;else if(tot > 2) res += tot - 2;ans = max(res, ans);}if(flag) dp[u] = 1;};if(n == 2) cout << "1\n";else {dfs(1, 1);cout << ans << "\n";}
}int main() {ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);int t;cin >> t;while(t -- ) solve();return 0;
}