艹逆🐎的构式学校能不能早点回去中考(25.4)

2025/4/1

R9T6

2025/4/3

学校不给我们放假我们自己放,爽爽爽。

原因是离烦与其余教练讨论我们何时回宏帆,结论是 5.18,我草你🐎的你是不是想让我们回去过 520 啊?早点回去会死啊,2+4 先走了我们在这里怎么办啊,一层楼就一个班周围的教室空荡荡的根闹鬼了一样,不知道哪个煞笔提的建议,SGT 的操作是上压力(说我们不重视中考) -> 吃定心丸(【数据删除】) -> 上压力(说最多 95 个自招) -> 吃定心丸(不用过多在乎中考)。

晚上 luogu 解密赛。

A 小小模拟调半天,long long 不会开看了半天才发现那个题里面有个惊世后人就是说 90 是因为爆 long long 了,致敬那道题是吧。

煞笔音游题丝毫不考虑非音游选手的感受。

还是穆恩斯达大佬音游玩的老六了好像差点过了 master

2025/4/4

煞笔膜你赛 T1 step 1 拍了 1h 发现是 step 2 假了。

发现此错误离考试结束还有 1.5h。

T2 是经典题但是没来得及写。

放一下 T2:

Statement

给定一个 \(1\sim n\) 的排列。

多次询问求经过 \(k\) 轮冒泡排序后 \(x\) 的位置,可以离线扫描线,只需要维护一个支持查询第 \(k\) 大的数据结构,用途是找有多少个数翻过了某一个点。

哦对了 \(10^5\) 可以放双 \(\log\) 过,可以学习 ycz 的主席树外面套二分,\(5\times 10 ^5\) 只能放单 \(\log\) 过,改法是把 ycz 的二分放主席树上,粘一下他的代码。

点击查看代码
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)

using namespace std;

const int MAXN = 5e5 + 5;
int n, q, a[MAXN], h[MAXN], s[MAXN];

struct Segment_Tree {
    int root[MAXN];
    int ls[MAXN * 30], rs[MAXN * 30], seg[MAXN * 30], cnt;

    inline void change(int &id, int p, int l, int r, int fx, int val) {
        id = ++cnt, ls[id] = ls[p], rs[id] = rs[p], seg[id] = seg[p];
        if (l == r) {
            seg[id] += val;
            return;
        }
        int mid = l + r >> 1;
        if (fx <= mid)
            change(ls[id], ls[p], l, mid, fx, val);
        else
            change(rs[id], rs[p], mid + 1, r, fx, val);
        seg[id] = seg[ls[id]] + seg[rs[id]];
    }

    inline int query(int &id, int l, int r, int fl, int fr) {
        if (!id)
            return 0;
        if (fl <= l && r <= fr)
            return seg[id];
        int mid = l + r >> 1, res = 0;
        if (fl <= mid)
            res += query(ls[id], l, mid, fl, fr);
        if (fr > mid)
            res += query(rs[id], mid + 1, r, fl, fr);
        return res;
    }
} SGT;

int main() {
    freopen("bubble.in", "r", stdin);
    freopen("bubble.out", "w", stdout);
    IOS;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i], s[a[i]] = i;
    for (int i = 1; i <= n; i++) {
        h[i] = SGT.query(SGT.root[i - 1], 1, n, a[i], n);
        SGT.change(SGT.root[i], SGT.root[i - 1], 1, n, a[i], 1);
    }
    cin >> q;
    for (int i = 1; i <= q; i++) {
        int k, x;
        cin >> k >> x;
        if (k <= h[s[x]])
            cout << s[x] - k;
        else {
            int res = s[x] - h[s[x]];
            k -= h[s[x]];
            int l = s[x], r = n, ans = -1, NNN = SGT.query(SGT.root[s[x]], 1, n, x, n);
            while (l <= r) {
                int mid = l + r >> 1;
                int num = SGT.query(SGT.root[mid], 1, n, x, n) - NNN;
                if (num >= k)
                    r = mid - 1, ans = mid - s[x] - num;
                else
                    l = mid + 1;
            }
            if (ans == -1)
                res = x + 1;
            cout << res + ans;
        }
        cout << '\n';
    }
    return 0;
}

Solution

点击查看代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for (int i = j; i <= k; ++i)
#define pre(i, j, k) for (int i = j; i >= k; --i)
#define PII pair<int, int>
#define fi first
#define se second
#define pb push_back
using namespace std;
using LL = long long;
const int N = 5e5 + 5;

int n, q, k, x, a[N], p[N], ans[N];

namespace SGT {
  int s[N << 2];
  #define ls (p << 1)
  #define rs (p << 1 | 1)
  #define mid (l + r >> 1)

  void up(int p) { s[p] = s[ls] + s[rs]; }
  void upd(int p, int ql, int qr, int x, int l = 1, int r = n) {
      if (ql <= l && r <= qr)
          return s[p] += x * (r - l + 1), void();
      if (ql <= mid)
          upd(ls, ql, qr, x, l, mid);
      if (qr > mid)
          upd(rs, ql, qr, x, mid + 1, r);
      up(p);
  }
  int ask(int p, int ql, int qr, int l = 1, int r = n) {
      if (ql > r || l > qr)
          return 0;
      if (ql <= l && r <= qr)
          return s[p];
      return ask(ls, ql, qr, l, mid) + ask(rs, ql, qr, mid + 1, r);
  }
  int find(int p, int x, int l = 1, int r = n) {
      if (l == r)
          return l;
      if (s[ls] >= x)
          return find(ls, x, l, mid);
      else
          return find(rs, x - s[ls], mid + 1, r);
  }
}  // namespace SGT

vector<PII> vec[N];

signed main() {
    FASTIO;
#ifdef gm

#else
    freopen("bubble.in", "r", stdin);
    freopen("bubble.out", "w", stdout);
#endif

    cin >> n;
    rep(i, 1, n) cin >> a[i], p[a[i]] = i;
    cin >> q;
    rep(i, 1, q) cin >> k >> x, vec[x].pb({ k, i });
    pre(i, n, 1) {
        for (auto ed : vec[i]) {
            int pre = SGT::ask(1, 1, p[i]), suf = n - pre - i;
            if (pre + suf < ed.fi)
                ans[ed.se] = i;
            else {
                if (ed.fi <= pre)
                    ans[ed.se] = p[i] - ed.fi;
                else
                    ans[ed.se] = SGT::find(1, ed.fi) - ed.fi;
            }
        }
        SGT::upd(1, p[i], p[i], 1);
    }
    rep(i, 1, q) cout << ans[i] << '\n';
    return 0;
}

2025/4/7

尼玛的学校又发力了打不了三国杀了。

2025/4/12

中间这段时间都是补以前的题没有新知,这天学了个 BSGS 感觉挺纽币的算法,粘一下板子:

点击查看代码
int BSGS(int a, int b, int p) {
    map<int, int> mp;
    int siz = ceil(sqrt(p));
    mp[b] = 0;
    rep(i, 1, siz) b = b * a % p, mp[b] = i;
    b = qkpow(a, siz, p);
    int tmp = 1;
    rep(i, 1, siz) {
        tmp = tmp * b % p;
        if (mp[tmp])
            return ((i * siz - mp[tmp]) % p + p) % p;
    }
    return -1;
}

作用是求一个同余方程的最小整数解 \(x\)

\[a^x\equiv b\pmod p \]

感觉很简单,但是做这类题需要推柿子很烦。

2025/4/14

P6499

离烦把这个题放在了状压的比赛中发现 \(N\leq 400\)

很逆天的题,你发现这个题不能贪心,原因是这个问的是这个人是否在另一个人所有可能的操作下都能有必胜策略,不能走一步看一步,你发现其实这个 Daniel 很好赢,我起初想了个贪心是当这个 Stjepan 走一步就把他儿子节点堵一个,这个方法其实是对的但是仍然需要 dp,你发现这个 Daniel 想要赢就是要在 \(k\) 次以内堵掉所有 \(dep\geq k\) 的路径,考虑 \(f_{x,S}\) 表示覆盖了前 \(x\) 条路径选择的深度集合为 \(S\) 是否能满足条件,你发现同一个深度选择超过一个点其实是无意义的,因为这个 Stjepan 早就跑下去了你堵了也没用。

然后这个状压非常大肯定接受不了,你又发现一个结论如果 \(k\) 很大我是不是可以在 Stjepan 下来之前就把所有路径全堵完了,肯定是可以的,\(k\) 要多大才能这样?结论是如果 \(k\geq \sqrt{n}\) 就可以,最劣情况就是在根下面挂 \(k\) 条长度为 \(k\) 的链。

然后预处理出 dfs 序表示 \([l_i,r_i)\) 就很好转移了。

注意用 bitset 压一下,本地数组好像开不下。

点击查看代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for (int i = j; i <= k; ++i)
#define pre(i, j, k) for (int i = j; i >= k; --i)
#define PII pair<int, int>
#define fi first
#define se second
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define int long long

using LL = long long;
using i64 = __int128;
using namespace std;

const int N = 8e2 + 5;
const int M = 21;
const int mod = 1e9 + 9;

#define cmax(a, b) (a = a > b ? a : b)
#define cmin(a, b) (a = a < b ? a : b)

int n, k, p, u, v, x, y, siz[N], dep[N], l[N], r[N], tt;
vector<int> e[N], vec[N];
bitset<1 << M> f[N];

void dfs(int x, int fa) {
	siz[x] = 1, dep[x] = dep[fa] + 1;
	if(dep[x] == k - 1) return l[x] = tt++, r[x] = ++tt, void();
	l[x] = tt;
	for(auto y : e[x]) {
		if(y == fa) continue;
		dfs(y, x), siz[x] += siz[y];
	}
	r[x] = tt;
}

signed main() {
	FASTIO; f[0][0] = 1, dep[0] = -2;
	
	cin >> n >> k; if(k * k >= n) cout << "DA\n", exit(0);
	rep(i, 1, n - 1) cin >> u >> v, e[u].pb(v), e[v].pb(u);
	dfs(1, 0);
	
	
	rep(i, 2, n) vec[l[i]].pb(i);
	
	rep(i, 0, tt) {
		rep(j, 0, (1 << k) - 1) {
			for(auto p : vec[i]) if(!(j >> dep[p] & 1)) f[r[p]][j | (1 << dep[p])] = f[r[p]][j | (1 << dep[p])] | f[i][j];
		}
	}
	
	int flg = 0;
	rep(i, 0, (1 << k) - 1) flg |= f[tt][i];
	cout << (flg ? "DA" : "NE");
	
	return 0;
}

因为瞟了眼题解还是粘一下这个题解。

哦对了三国杀开了个小号叫 Walrus,现在这个狗卡的新手福利也是够逆天的,我第一个十连出了两个我大号都没有的 360 符武将,还送大小乔,SP 马超和葛玄不知道能不能嫖到,前者应该没问题葛玄要 110 个武将才送,差评!

2025/4/15

P8392

前言

做了 2.5 min 终于过上题了,这个题比较新是可撤销的多重背包。

做题的时候旁边的 Xdik 最开始说我看题解我说他看鸡爪他还不耐烦,他做一道 arc 的题对着一个大小为 4 的菊花看了半天,这个菊花不就是鸡爪吗?关键是他还用鼠标拖来拖去,还说我不思考,难以评价!

这是 Xdik 看的鸡爪:

upd:才发现原题有样例解释,样例解释里面详细的给出了 3 种染色方案,不知道 Xdik 选手是电脑出 bug 了图片没加载出来还是怎么回事。


逃不掉多重背包,数据范围容量及其大,一般的背包显然做不到,考虑怎么把这个容量优化掉,由于题目中说了最后背包的容量必须是 \(l\),并且 \(1\leq m \leq 300\)?!你发现这个背包数组有很多东西是用不上的!

考虑贪心优化一下哪些用不上,先全选,然后根据此时的容量贪心调整(小了先丢最小的,大了先丢最大的),这样调整后的容量一定控制在 \([l-m,l+m]\) 之间,你发现这个区间长度最多 \(2\times m\),然后就解决了空间问题,然后还有一个问题,同一个容量 \(S\) 一定不会出现多次,即一个元素不会被加入删除多次,否则此操作完全可以拿到最前面而非调整后,这样一定是不优的。

哦对了这个写法是提前把负数全选了,后面选择正数相当于也选择了负数。

然后用多重背包调整,你就设 \(f_i\) 表示当前容量为 \(i\) 最大价值,答案就是预选的非整数加上 \(f_{l+m}\),因为每个物品还是很多所以还是要二进制优化。

点击查看代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for (int i = j; i <= k; ++i)
#define pre(i, j, k) for (int i = j; i >= k; --i)
#define PII pair<int, int>
#define fi first
#define se second
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define int long long

using LL = long long;
using i64 = __int128;
using namespace std;

const int N = 2e5 + 5;
const int M = 21;
const int mod = 1e9 + 9;

#define cmax(a, b) (a = a > b ? a : b)
#define cmin(a, b) (a = a < b ? a : b)
#define cadd(a, b) (a = (a + b) % mod)

int n, l, x, m, k, sum, ret;
int a[N], f[N], b[N], ca[N], cb[N];

void trans(int v, int w, int id) {
	if(id == 1) pre(i, k - v, 0) cmax(f[i + v], f[i] + w);
	if(id == 2) rep(i, v, k) cmax(f[i - v], f[i] + w);
}

void ins(int v, int s, int w) {
	s = min(s, n * 2 + 1);
	if(v > 0) {
		int pw = 1;
		while(pw + pw < s) pw <<= 1, v <<= 1, w <<= 1;
		while(1) {
			trans(v, w, 1);
			s -= pw;
			if(pw == 1) break;
			pw >>= 1, v >>= 1, w >>= 1;
		}
		if(s > 0) trans(v * s, w * s, 1);
	}
	else {
		v = -v;
		int pw = 1;
		while(pw + pw < s) pw <<= 1, v <<= 1, w <<= 1;
		while(1) {
			trans(v, w, 2);
			s -= pw;
			if(pw == 1) break;
			pw >>= 1, v >>= 1, w >>= 1;
		}
		if(s > 0) trans(v * s, w * s, 2);
	}
}

signed main() {
	FASTIO;
	memset(f, 0xcf, sizeof f);
	
	
	cin >> n >> l; m = n * n, k = n * n * 2, f[m] = 0;
	pre(i, n, 1) cin >> a[i], l += a[i] * i, ret += a[i];
	if(l < 0) cout << "impossible", exit(0);
	cin >> x, ret += x;
	rep(i, 1, n) cin >> b[i];
	
	rep(i, 1, n) {
		if(b[i] * i <= l) l -= b[i] * i, ret += (cb[i] = b[i]);
		else {
			ret += (cb[i] = l / i), l -= cb[i] * i;
			break;
		}
	}
	
	pre(i, n, 1) {
		if(a[i] * i <= l) l -= a[i] * i, ret -= (ca[i] = a[i]);
		else {
			ret -= (ca[i] = l / i), l -= ca[i] * i;
			break;
		}
	}
	
//	cout << '!' << ret << '\n';
	
	rep(i, 1, n) {
//		cout << a[i] << ' ' << ca[i] << ' ' << b[i] << ' ' << cb[i] << '\n';
		if(cb[i]) ins(-i, cb[i], -1);
		if(b[i] - cb[i]) ins(i, b[i] - cb[i], 1);
		if(ca[i]) ins(-i, ca[i], 1);
		if(a[i] - ca[i]) ins(i, a[i] - ca[i], -1);
	}

	if(l > m || f[l + m] < -m) cout << "impossible";
	else cout << ret + f[l + m];

	return 0;
}

2025/4/17

ARC102e

点击查看代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)

#define rep(i, j, k) for (int i = j; i <= k; ++i)

#define pre(i, j, k) for (int i = j; i >= k; --i)

#define PII pair<int, int>

#define fi first

#define se second

#define pb push_back

#define lb lower_bound

#define ub upper_bound

#define int long long


using LL = long long;
using i64 = __int128;
using namespace std;

const int N = 5e3 + 5;
const int M = 21;
const int mod = 1e9 + 7;

#define cmax(a, b) (a = a > b ? a : b)

#define cmin(a, b) (a = a < b ? a : b)

#define cadd(a, b) (a = (a + b) % mod)

#define cdel(a, b) (a = ((a - b) % mod + mod) % mod)


int f[N][N], g[N], n, m, u, v, s[N], siz[N], ret;
vector<int> e[N];
void dfs(int x, int fa) {
    siz[x] = f[x][1] = 1;
    for (auto y : e[x]) {
        if (y == fa)
            continue;
        dfs(y, x);
        rep(i, 0, siz[x] + siz[y]) g[i] = 0;
        rep(i, 0, siz[x]) {
            rep(j, 0, siz[y]) {
                cdel(g[i], f[x][i] * f[y][j] % mod * s[j] % mod);
                cadd(g[i + j], f[x][i] * f[y][j] % mod);
            }
        }
        rep(i, 0, siz[x] + siz[y]) f[x][i] = g[i];
        siz[x] += siz[y];
    }
}

signed main() {
    FASTIO;

    cin >> n;
    rep(i, 1, n - 1) cin >> u >> v, e[u].pb(v), e[v].pb(u);
    s[0] = 1;
    rep(i, 2, n) s[i] = s[i - 2] * (i - 1) % mod, ++i;

    dfs(1, 0);

    rep(i, 1, n) cadd(ret, f[1][i] * s[i]);
    cout << ret;
    return 0;
}

2025/4/19

P6837

很逆天的构造题,当时 IOI 所有人都是 92,事实上 92 很好做到。

Sub1

\(n-1\) 次询问,每次询问 \(0\)\(i\) 即可。

Sub2

\(2+\lceil \dfrac{n-3}{2}\rceil\) 次询问,先利用 Sub1 的策略找到两个相同的蘑菇,根据抽屉原理,三个就必定会有两个一样的,接着对未知蘑菇两两配对采用 \(AXAX\) 类型的询问,其中两个 \(A\) 是已知的相同蘑菇,这样根据 use_machine 返回的结果 \(t\) 就可以判断出两个 \(X\) 分别是什么。

Sub3

假设拥有大量的 \(A\)\(B\),则可以考虑构造出 \(AXAXAXA\) 的序列,此序列中每当有一个 \(X\)\(B\) 那么就会给返回值增加 \(2\) 的贡献,前面先按 Sub2 的策略找出 \(B\) 个蘑菇,然后后面每次询问就可以找到一堆。

此方法大约可以获得 52-80 pts。

Sub4

每次 Sub3 的询问可以在末尾多加一个根据奇偶性判断末尾的这个是什么,可以得到 92 pts。

Sub5

好像要动态调块长,不清楚

2025/4/27

P1450

容斥,钦定 \(i\) 个不合法的硬币计算即可,预处理出背包。

posted @ 2025-04-03 17:27  Iron_Spade  阅读(43)  评论(0)    收藏  举报