csp模拟10[欧几里得的噩梦, 清扫, 购物, ants]
T1 欧几里得的噩梦
嘴上说着要线性基,身体上还是很诚实得不要了嘛
-
这个题其实仅仅是用了线性基的思想,和它没有半毛钱关系
-
首先对于题目要求的限制大小最小以及字典序最小来分析
-
大小最小
- 说明了我们现在选的数已经很够了,再向里边加任何一个数都是多余的,即是对于现在我们想加入一个数,但是它如果能被我选完的数字异或出来,那他就可以跳过,没有加的必要,这样一通维护之后,我们就可以保证不冗余,也就是大小最小
-
字典序最小
其实根本不用考虑的说,因为我们是从前向后枚举的
对于我已经选择了的数\(a, b, c\),我们考虑新来了一个数字\(k\),
\(a \ xor \ b \ xor \ c = k\) ,而\(k\)的字典序比\(a\)小(假设\(a\)为其中字典序最大的),我们显然可以移项得\(k \ xor \ b \ xor \ c = a\),所以把\(k\)加进去,把\(a\)踢出来,这样字典序最小,同时大小不变
-
-
考虑怎么判断一个数可以被异或出来
-
因为每一个数的\(1\)最多有两位是\(1\),所以可以将这个判断抽象为判断图的联通,用并查集维护, 具体操作 :
- 我们可以建立一个超级原点,将一个数字和它连边表示它存在于子集中,或者是可以通过子集中的数字得到($eg : $子集中有 \(x, x \ xor \ y\),那么就相当于是有了\(y\),处理\(x\)和\(x \ xor \ y\)时会路径压缩以及连边,做到这个效果),当然,这个实际上是表示,我可以在子集中得到\(x\)位上是\(1\)的数
- 对于有二进制位两个\(1\)的数字,我们可以判断这两个数字是否连边,如果未连边,就将两者连边,表示我加进去了这两个数异或的值
这里注意并非是将两者和超级原点连边,因为和原点连边表示存在这两个数,而这里表示我有\(x \ xor y\),就是我有\(x \ xor y\)我不一定有\(x\),我也不一定有\(y\)
here
#include <bits/stdc++.h>
#define LL long long
#define Re register int
#define LD double
#define mes(x, y) memset(x, y, sizeof(x))
#define cpt(x, y) memcpy(x, y, sizeof(x))
#define fuc(x, y) inline x y
#define fr(x, y, z)for(Re x = y; x <= z; x ++)
#define fp(x, y, z)for(Re x = y; x >= z; x --)
#define delfr(x, y, z)for(Re x = y; x < z; x ++)
#define delfp(x, y, z)for(Re x = y; x > z; x --)
#define frein(x) freopen(#x ".in", "r", stdin)
#define freout(x) freopen(#x ".out", "w", stdout)
#define ki putchar('\n')
#define fk putchar(' ')
#define WMX aiaiaiai~~
#define pr(x, y) pair<x, y>
#define mk(x, y) make_pair(x, y)
#define pb(x) push_back(x)
#define re(x) return x
#define sec second
#define fst first
using namespace std;
namespace kiritokazuto{
auto read = [](){
LL x = 0;
int f = 1;
char c;
while (!isdigit(c = getchar())){ if (c == '-')f = -1; }
do{ x = (x << 1) + (x << 3) + (c ^ 48); } while (isdigit(c = getchar()));
return x * f;
};
template <typename T> fuc(void, write)(T x){
if (x < 0)putchar('-'), x = -x;
if (x > 9)write(x / 10); putchar(x % 10 | '0');
}
}
using namespace kiritokazuto;
const int maxn = 5e5 + 10, Mod = 1e9 + 7;
const LL Inf = 2147483647;
int n, m;
int fa[maxn];
fuc(LL ,qpow)(LL x, LL y) {
LL res = 1;
while(y) {
if(y & 1)res = (res * x) % Mod;
x = (x * x) % Mod;
y >>= 1;
}
return res % Mod;
}
int ans = 0;
int vis[maxn];
fuc(int, find)(int x){return (x == fa[x]) ? x : fa[x] = find(fa[x]);}
signed main(){
n = read(), m = read();
fr(i, 0, m)fa[i] = i;
fr(i, 1, n) {
int type = read();
if(type == 1) {
int x = read();
if(find(x) == find(0))continue;
else ans++, fa[find(x)] = find(0), vis[i] = 1;
}
if(type == 2){
int x = read(), y = read();
if(find(x) == find(y)) {
continue;
}else
{
ans++;
fa[find(x)] = find(y);
vis[i] = 1;
}
}
}
write((LL)qpow(2, ans) % Mod), fk;
write(ans);
ki;
fr(i, 1, n){
if(vis[i])printf("%d ", i);
}
return 0;
}
T2 清扫
- 奶奶的,这个小题,
巧滴hen(四声)。
我们设当前子树的根节点为 \(rt\), 它有的石子数为 \(a[rt]\),子树总共需要抵消的石子数之和为 \(sum\) (也就是所有子树的 \(sz\) 和),子树中最多的一个的石子数为 \(Max\),子树内部相互抵消的次数为 \(x\) (即不同子树两两组队,抵消)。
- 考虑两种抵消情况对 \(rt\) 的影响:
- 子树内部相互抵消:
- 每次 \(sum - 2\),\(a[rt] - 1\)。
- 子树内和外部抵消:
- 每次 \(sum - 1\),\(a[rt] - 1\)。
- 子树内部相互抵消:
- 首先如果我的 \(sum\) 直接就 比我的 \(a[rt]\) 大,显然不行。
- 我们想让当前子树可以消完,那么我子树里还需要消除的次数必须和我还需的次数相等,如果多则会把我干成负的,少我就把他们干成负的,显然不行。
- 所以我们可以解一个方程 \(sum - 2 \times x = a[rt] - x\),得出:
\[sum - a[rt] = x
\]
- 所以我(既是我的,也是我子树的)剩下的所需要的次数为:
\[a[rt] - x = 2 \times a[rt] - sum
\]
- 当然,这里我们还需要考虑一种非法情况: 如果我有一颗特别大的子树,那么我其他的子树每一个人都和他消一次是最优的,所以除去自己的石子,它还应该被消 \(sum - Max\) 次, 所以它最终剩下:
\[Max - (sum - Max) = 2 \times Max - sum
\]
- 个石子(当然,这个的值有可能是负的)。那么我们需要判断一下,我根节点现在还需要消除的次数应该是要比它多的,否则是不行的,会有负数,所以这个题就干完了。
- 当然,对于两个点的情况可以特判一下,我的根应该选一个度数大于 \(1\) 的节点,以及最终我的 \(sz[rt] = 0\) 是必须的。
- 之后就 \(dfs\) 一遍统计答案就行。
here
#include <bits/stdc++.h>
#define LL long long
#define Re register int
#define LD double
#define mes(x, y) memset(x, y, sizeof(x))
#define cpt(x, y) memcpy(x, y, sizeof(x))
#define fuc(x, y) inline x y
#define fr(x, y, z)for(Re x = y; x <= z; x ++)
#define fp(x, y, z)for(Re x = y; x >= z; x --)
#define delfr(x, y, z)for(Re x = y; x < z; x ++)
#define delfp(x, y, z)for(Re x = y; x > z; x --)
#define frein(x) freopen(#x ".in", "r", stdin)
#define freout(x) freopen(#x ".out", "w", stdout)
#define ki putchar('\n')
#define fk putchar(' ')
#define WMX aiaiaiai~~
#define pr(x, y) pair<x, y>
#define mk(x, y) make_pair(x, y)
#define pb(x) push_back(x)
#define re(x) return x
#define sec second
#define fst first
using namespace std;
namespace kiritokazuto{
auto read = [](){
LL x = 0;
int f = 1;
char c;
while (!isdigit(c = getchar())){ if (c == '-')f = -1; }
do{ x = (x << 1) + (x << 3) + (c ^ 48); } while (isdigit(c = getchar()));
return x * f;
};
template <typename T> fuc(void, write)(T x){
if (x < 0)putchar('-'), x = -x;
if (x > 9)write(x / 10); putchar(x % 10 | '0');
}
}
using namespace kiritokazuto;
const int maxn = 2e5 + 100, Mod = 1e9 + 7;
const LL Inf = 2147483647;
int n;
vector <int> wmx[maxn];
int sz[maxn], son[maxn];
int a[maxn];
int rt = 1;
fuc(bool, dfs)(int x, int pre) {
if(wmx[x].size() == 1)return sz[x] = a[x], 1;
for(auto to : wmx[x]) {
if(to != pre) {
if(!dfs(to, x))re(0);
sz[x] += sz[to];
if(!son[x] || sz[son[x]] < sz[to])son[x] = to;
}
}
if(sz[x] < a[x])re(0);
int tmp = (sz[son[x]] * 2) - sz[x];
sz[x] = (a[x] * 2) - sz[x];
return sz[x] >= max(tmp, 0);
}
signed main() {
n =read();
fr(i, 1, n)a[i] = read();
delfr(i, 1, n) {
int x = read(), y = read();
wmx[x].pb(y);
wmx[y].pb(x);
}
while(wmx[rt].size() == 1)rt++;
if(rt == n + 1) {
(a[1] == a[2]) ? puts("YES") : puts("NO");
re(0);
}
(dfs(rt, 0) && sz[rt] == 0 ? puts("YES") : puts("NO"));
}
T3 购物
应该是很多人的场切题目了,这玩意照旁边 \(whpan\) 说大力分类讨论就行,em
- 其实我不想写了..
- 对于一个数 \(x\), 那么它的贡献区间就是 \([\left \lceil \frac{x}{2} \right \rceil,x]\), 我们可以考虑每一个数的贡献都贡献在了哪里:
- 首先维护一个前缀和\(sum\), 它就是我前\(i - 1\)个数贡献的上界(显然),
然后分类讨论
- 首先维护一个前缀和\(sum\), 它就是我前\(i - 1\)个数贡献的上界(显然),
- 由于我懒得写,所以看 \(whp\)的吧
here
#include <bits/stdc++.h>
#define LL long long
#define Re register int
#define LD double
#define mes(x, y) memset(x, y, sizeof(x))
#define cpt(x, y) memcpy(x, y, sizeof(x))
#define fuc(x, y) inline x y
#define fr(x, y, z)for(Re x = y; x <= z; x ++)
#define fp(x, y, z)for(Re x = y; x >= z; x --)
#define delfr(x, y, z)for(Re x = y; x < z; x ++)
#define delfp(x, y, z)for(Re x = y; x > z; x --)
#define frein(x) freopen(#x ".in", "r", stdin)
#define freout(x) freopen(#x ".out", "w", stdout)
#define ki putchar('\n')
#define fk putchar(' ')
#define WMX aiaiaiai~~
#define pr(x, y) pair<x, y>
#define mk(x, y) make_pair(x, y)
#define pb(x) push_back(x)
#define re(x) return x
#define sec second
#define fst first
using namespace std;
namespace kiritokazuto{
auto read = [](){
LL x = 0;
int f = 1;
char c;
while (!isdigit(c = getchar())){ if (c == '-')f = -1; }
do{ x = (x << 1) + (x << 3) + (c ^ 48); } while (isdigit(c = getchar()));
return x * f;
};
template <typename T> fuc(void, write)(T x){
if (x < 0)putchar('-'), x = -x;
if (x > 9)write(x / 10); putchar(x % 10 | '0');
}
}
using namespace kiritokazuto;
const int maxn = 3e5 + 100, Mod = 1e9 + 7;
const LL Inf = 1e18, ord = 1e10;
int n;
unordered_map <LL, int> Map;
int a[maxn];
LL up = 0;
fuc(void, work1)();
fuc(void, work2)();
fuc(void, work3)();
LL down = 0;
LL sum = 0;
LL ans = 0;
signed main() {
n = read();
fr(i, 1, n) a[i] = read(), sum += a[i];
// if(n == 1) {
// work1();
// re(0);
// }
// if(n == 2) {
// work2();
// re(0);
// }
// if(n == 3) {
// work3();
// re(0) ;
// }
sort(a + 1, a + n + 1);
fr(i, 1, n) {
down = (LL)ceil(1.0 * a[i] / 2);//下界
if(down > up)ans = ans + up + a[i] - down + 1;//大于上界
else ans += a[i];
up += a[i];
}
write(ans);
re(0);
}
fuc(void, work1)() {
LL tmp = (LL)ceil(1.0 * sum / 2);
write(sum - tmp + 1);
return;
}
fuc(void, work2)() {
LL tmp1 = (LL)ceil(1.0 * a[1] / 2);
LL tmp2 = (LL)ceil(1.0 * a[2] / 2);
LL tmp3 = (LL)ceil(1.0 * sum / 2);
LL ans = 0 ;
fr(i, tmp1, a[1]) {
if(Map.find(i) == Map.end()) {
Map[i]++;
ans++;
}
}
fr(i, tmp2, a[2]) {
if(Map.find(i) == Map.end()) {
Map[i]++;
ans++;
}
}
fr(i, tmp3, sum) {
if(Map.find(i) == Map.end()) {
Map[i]++;
ans++;
}
}
write(ans);
return;
}
fuc(void, work3)() {
LL tmp1 = (LL)ceil(1.0 * a[1] / 2);
LL tmp2 = (LL)ceil(1.0 * a[2] / 2);
LL tmp3 = (LL)ceil(1.0 * a[3] / 2);
LL tmp4 = (LL)ceil(1.0 * (a[1] + a[2]) / 2);
LL tmp5 = (LL)ceil(1.0 * (a[2] + a[3]) / 2);
LL tmp6 = (LL)ceil(1.0 * (a[1] + a[3]) / 2);
LL tmp7 = (LL)ceil(1.0 * sum / 2);
LL ans = 0 ;
fr(i, tmp1, a[1]) {
if(Map.find(i) == Map.end()) {
Map[i]++;
ans++;
}
}
fr(i, tmp2, a[2]) {
if(Map.find(i) == Map.end()) {
Map[i]++;
ans++;
}
}
fr(i, tmp3, a[3]) {
if(Map.find(i) == Map.end()) {
Map[i]++;
ans++;
}
}
fr(i, tmp4, a[1] + a[2]) {
if(Map.find(i) == Map.end()) {
Map[i]++;
ans++;
}
}
fr(i, tmp5, a[2] + a[3]) {
if(Map.find(i) == Map.end()) {
Map[i]++;
ans++;
}
}
fr(i, tmp6, a[1] + a[3]) {
if(Map.find(i) == Map.end()) {
Map[i]++;
ans++;
}
}
fr(i, tmp7, sum) {
if(Map.find(i) == Map.end()) {
Map[i]++;
ans++;
}
}
write(ans);
return;
}
T4 ants
- permu升级版板子,也就是回滚莫队板子
here
#include <bits/stdc++.h>
#define LL long long
#define Re register int
#define LD double
#define mes(x, y) memset(x, y, sizeof(x))
#define cpt(x, y) memcpy(x, y, sizeof(x))
#define fuc(x, y) inline x y
#define fr(x, y, z)for(Re x = y; x <= z; x ++)
#define fp(x, y, z)for(Re x = y; x >= z; x --)
#define delfr(x, y, z)for(Re x = y; x < z; x ++)
#define delfp(x, y, z)for(Re x = y; x > z; x --)
#define frein(x) freopen(#x ".in", "r", stdin)
#define freout(x) freopen(#x ".out", "w", stdout)
#define ki putchar('\n')
#define fk putchar(' ')
#define WMXX aiaiaiai~~
#define pr(x, y) pair<x, y>
#define mk(x, y) make_pair(x, y)
#define pb(x) push_back(x)
#define re(x) return x
#define sec second
#define fst first
using namespace std;
namespace kiritokazuto{
auto read = [](){
LL x = 0;
int f = 1;
char c;
while (!isdigit(c = getchar())){ if (c == '-')f = -1; }
do{ x = (x << 1) + (x << 3) + (c ^ 48); } while (isdigit(c = getchar()));
return x * f;
};
template <typename T> fuc(void, write)(T x){
if (x < 0)putchar('-'), x = -x;
if (x > 9)write(x / 10); putchar(x % 10 | '0');
}
}
using namespace kiritokazuto;
const int maxn = 1e5 + 10, Mod = 1e9 + 7;
const LL Inf = 2147483647;
int n, m;
int bel[maxn];
int a[maxn];
int ans[maxn];
int st[maxn], ed[maxn];
int sq;
struct Node {
int l, r, id;
friend bool operator < (Node A, Node B) {
return (bel[A.l] == bel[B.l]) ? A.r < B.r : bel[A.l] < bel[B.l];
}
}q[maxn];
struct WMX {
int type, pos, val;
WMX(){};
WMX(int x, int y, int z){type = x, pos = y, val = z;};
}qq[maxn * 2];
int lid[maxn], rid[maxn];
int top;
signed main(){
n = read();
m = read();
sq = sqrt(n);
fr(i, 1, n) {
a[i] = read();
// bel[i] = (i - 1) / sq + 1;
}
fr(i, 1, m) {
q[i].l = read();
q[i].r = read();
q[i].id = i;
}
fr(i, 1, sq) {
st[i] = (n / sq) * (i - 1) + 1;
ed[i] = (n / sq) * i;
if(i == sq)ed[i] = n;
fr(j, st[i], ed[i])bel[j] = i;
}
sort(q + 1, q + m + 1);
int r = 0, sum = 0;
fr(i, 1, m) {
if(bel[q[i].l] != bel[q[i - 1].l]) {
sum = 0;
fr(i, 1, n)lid[a[i]] = rid[a[i]] = 0;
r = ed[bel[q[i].l]];
}
while(r < q[i].r) {
r ++;
lid[a[r]] = lid[a[r] - 1] + 1;
rid[a[r]] = rid[a[r] + 1] + 1;
int tmp = lid[a[r]] + rid[a[r]] - 1;
sum = max(sum, tmp);
lid[a[r] + rid[a[r]] - 1] = rid[a[r] - lid[a[r]] + 1] = tmp;
}
int res = sum;
top = 0;
int now = min(q[i].r, ed[bel[q[i].l]]);
fr(j, q[i].l, now) {
lid[a[j]] = lid[a[j] - 1] + 1;
rid[a[j]] = rid[a[j] + 1] + 1;
int tmp = lid[a[j]] + rid[a[j]] - 1;
res = max(res, tmp);
qq[++top]=WMX(1, a[j] + rid[a[j]] - 1,lid[a[j] + rid[a[j]] - 1]);
qq[++top]=WMX(2, a[j] - lid[a[j]] + 1, rid[a[j] - lid[a[j]] + 1]);
lid[a[j] + rid[a[j]] - 1] = rid[a[j] - lid[a[j]] + 1] = tmp;
}
fp(i, top, 1) {
if(qq[i].type == 1)lid[qq[i].pos] = qq[i].val;
else rid[qq[i].pos] = qq[i].val;
}
fr(j, q[i].l, now)lid[a[j]] = rid[a[j]] = 0;
ans[q[i].id] = res;
}
fr(i, 1, m)write(ans[i]), ki;
re(0);
}
愿你在冷铁卷刃之前,得以窥见天光