闲话 23.1.18

闲话

魔圆要出新剧场版了!link
当然我一如既往地不会去自找刀吃的
老虚的刀 劲太足了

前几天看了 EI 的新博文
虽然很有旧基金会的感觉 但其实精神内核才是最重要的吧

逃避现实并不可耻,相反,不逃避现实才是可耻的,因为只有一种不逃避现实的方法,就是活在梦里。

共勉。

今日模拟赛

\(\text{T1 (connect)}\)

我们交换了近千条信息,感觉心的距离只近了一厘米。—— 《秒速五厘米

可以发现不是两个端点都在边界上的线段等于不存在,因为我们可以贴着他的边绕过去。因此我们只需要考虑两端点都在边界上的线段。

这一部分有交当且仅当两条线段彼此交叉,也就是对于一条线段两端点截断的其中一个区域,存在一条线段有且仅有一个端点在其中。
这个可以将边界单独拿出来断成序列,在上面做类似括号序列的判定。

总时间复杂度 \(O(n \log n)\),瓶颈在排序和离散化。

code
#include <bits/stdc++.h>
using namespace std; 
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
#define multi int T; cin >> T; while ( T -- )
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 4e5 + 10;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
int n, m, w, t1, t2, t3, t4, fa[N], buk[N], stk[N], top;
vp vec;
struct __Hsh {
	size_t operator() (const pii& __Pair) const {
		return __Pair.first * 131 + __Pair.second;
	}
}; unordered_map<pii, int, __Hsh> mp;

int find(int u) { return u == fa[u] ? u : fa[u] = find(fa[u]); }
void merge(int u, int v) { 
	u = find(u), v = find(v);
	fa[u] = v;
} 
int get(pii x) {
	if (x.first == 0) return x.second;
	if (x.second == m) return x.first + m;
	if (x.first == n) return m - x.second + m + n;
	if (x.second == 0) return n - x.first + m + m + n;
	return -1;
}

signed main() {
	iot;
	file(connect);
	iota(fa, fa + N, 0);
	cin >> n >> m >> w;
	rep(i,1,w) {
		cin >> t1 >> t2 >> t3 >> t4;
		mp[{t1, t2}] = mp.size() + 1;
		mp[{t3, t4}] = mp.size() + 1;
		merge(mp.size(), mp.size() - 1);
		if(t1 == 0 or t1 == n or t2 == 0 or t2 == m) 
			vec.eb(t1, t2);
		if(t3 == 0 or t3 == n or t4 == 0 or t4 == m) 
			vec.eb(t3, t4);
	}
	sort(vec.begin(), vec.end(), [&](pii x, pii y) { return get(x) < get(y); });
	for (auto v : vec) ++ buk[find(mp[v])];
	for (auto v : vec) {
		if (top and find(mp[v]) == stk[top]) -- buk[stk[top]];
		else stk[++ top] = find(mp[v]);
		if (top and buk[stk[top]] == 1) -- top;
	} 
	top ? puts("NO") : puts("YES");
} 


\(\text{T2 (liveon)}\)

Keep us deep down in your hearts and please keep living on - 《Living On

简单树剖题。由经典数论结论可以知道我们可以把这一段的 \(a_i\) 乘起来,总体让 \(z\) 除。
这样我们就把原问题转化成了单点修改区间乘积了。熟悉一些经典问题的同学也可以知道,这里如果换成区间把值为 \(x\) 的元素改成 \(y\) 是可以做的,暴力的复杂度可以做到 \(\widetilde O(n\sqrt{n \log n})\)。挺劣的,有没有方法突破根号呢?

话题拉回来,这里有一个问题:一堆 \(a_i\) 的乘积很可能爆 long long,采用 double 存可能丢精度。这时又是经典转化,我们将 \(a_i\)\(\log\) 用于和 \(z\) 比较大小。这样的精度是能保证的,而且当 \(\sum \log a_i \le \log z\) 时显然 \(\prod a_i\) 不会爆 long long

这样我们就能在 \(O(n\log^2 n)\) 的复杂度内解决问题了。

code
#include <bits/stdc++.h>
using namespace std; 
#define int long long
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
#define multi int T; cin >> T; while ( T -- )
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
int n, q, typ, t1, t2;
int dfn[N], idfn[N], iid[N], dep[N], son[N], siz[N], fa[N], top[N], stp;
ll t3, a[N];
vector<tuple<int,int,ll> > g[N];

struct dat {
	ll prf; ld rec;
	dat(ll prf = 1, ld rec = 0) : prf(prf), rec(rec) { }
	dat operator * (const dat& b) { return { prf * b.prf, rec + b.rec }; }
};

struct SegmentCitrus {
	#define ls (p << 1)
	#define rs (p << 1 | 1)
	#define prf(p) seg[p].prf
	#define rec(p) seg[p].rec

	dat seg[N << 2];

	void build(int p = 1, int l = 1, int r = n) {
		if (l == r) {
			prf(p) = a[idfn[l]];
			rec(p) = log2l(a[idfn[l]]);
			return;
		} int mid = l + r >> 1;
		build(ls, l, mid);
		build(rs, mid + 1, r);
		seg[p] = seg[ls] * seg[rs];
	}

	void upd(int p, int l, int r, int pos, ll v) {
		if (l == r) {
			prf(p) = v; rec(p) = log2l(v);
			return ;
		} int mid = l + r >> 1;
		if (pos <= mid) upd(ls, l, mid, pos, v);
		else upd(rs, mid + 1, r, pos, v);
		seg[p] = seg[ls] * seg[rs];
	}

	dat query(int p, int l, int r, int L, int R) {
		if (L > R) return dat();
		if (L <= l and r <= R) return seg[p];
		int mid = l + r >> 1; dat ret;
		if (L <= mid) ret = ret * query(ls, l, mid, L, R);
		if (mid < R) ret = ret * query(rs, mid + 1, r, L, R);
		return ret;
	}
} Tr;

void dfs1(int u, int fat) {
	siz[u] = 1, son[u] = 0, fa[u] = fat;
	dep[u] = dep[fat] + 1;
	for (auto [v, w, id] : g[u]) if (v != fat) {
		a[v] = w, iid[id] = v;
		dfs1(v, u), siz[u] += siz[v];
		if (siz[v] > siz[son[u]]) 
			son[u] = v;
	} 
}

void dfs2(int u, int ctp) {
	top[u] = ctp, dfn[u] = ++ stp, idfn[stp] = u;
	if (son[u]) dfs2(son[u], ctp);
	for (auto [v, w, id] : g[u]) if (v != fa[u] and v != son[u])
		dfs2(v, v);
} 

dat query(int u, int v) {
	dat ret; if (u == v) return dat();
	while (top[u] != top[v]) {
		if (dep[top[u]] < dep[top[v]]) swap(u, v);
		ret = ret * Tr.query(1, 1, n, dfn[top[u]], dfn[u]);
		u = fa[top[u]];
	} if (dep[u] > dep[v]) swap(u, v);
	ret = ret * Tr.query(1, 1, n, dfn[u] + 1, dfn[v]);
	return ret;
}

signed main() {
	iot; file(liveon);
	cin >> n >> q;
	rep(i,1,n-1){
		cin >> t1 >> t2 >> t3;
		g[t1].eb(t2, t3, i);
		g[t2].eb(t1, t3, i);
	} dfs1(1, 0); dfs2(1, 1);
	Tr.build();
	while ( q -- ) {
		cin >> typ >> t1 >> t2;
		if (typ == 1) {
			cin >> t3;
			dat v = query(t1, t2);
			if (v.rec > log2l(t3)) cout << 0 << '\n';
			else cout << (t3 / v.prf) << '\n';
		} else {
			Tr.upd(1, 1, n, dfn[iid[t1]], t2);
		}
	} 
}


\(\text{T3 (flower)}\)

飘飘然地 我们连声音也尽然忘却 —— 《偷春人

对于这类问题,我们有一个经典的观察,就是提取两对的顺序关系,然后对原序列施这个偏序下的排序,就可以得到一种解法。因此我们观察 \(n = 2\) 的情况:

  1. \((1, 2), (3, 4)\)
    这时定是无解的。
  2. \((1, 3), (2, 4)\)
    我们需要两侧的大小变化是不同的,因此这一对必定翻转情况不同。
  3. \((1, 3), (2, 4)\)
    \(2.\),这一对必定翻转情况相同。
  4. \((1, 4), (2, 3)\)
    这俩无关,不论如何都可以通过交换位置得到合法。

因此我们能建出 \(O(n^2)\) 条边的图,直接做 2-sat 的复杂度是 \(O(n^2)\) 的。

考虑优化。我们对 \(\min(a_i, b_i)\) 做排序,用一个 set 维护 \(\max(a_i ,b_i)\),然后我们可以根据最小的 \(\max(a_i, b_i)\) 合并。容易发现这样会找到 \(O(n)\) 条有用的边,这些边能确定答案。

总时间复杂度 \(O(n\log n)\)

其实没理解,我也不理解我贺的这份代码。
可以问 lyin 讲题解做法。
如何 \(O(n)\)?听说和这个做法完全不同。

code
#include <bits/stdc++.h>
using namespace std; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
#define multi int T; cin >> T; while ( T -- )
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f;
int n, other[N], t1, t2, ans;
struct pii {
    int first, second;
    bool operator < (const pii& b) const {
        return first < b.first;
    }
}; set<pii> st;

int calc() {
    int tot = 1;
    auto d = *st.begin(); 
    int ans = d.second, r = other[d.first];
    st.erase(st.begin()), st.erase({r, 0});
    for (int ptr = 1; st.size(); ptr ^= 1) {
        if (ptr) {
            int r2 = 0;
            while (st.size()) {
                auto itr = -- st.end();
                if (itr->first <= r) break;
                tot ++, ans += itr->second;
                int t1 = itr->first, t2 = other[t1];
                if (t2 < r2 or t2 > r) return -1;
                r2 = t2;
                st.erase(itr), st.erase({t2, 0});
            } if (r2 == 0) break;
            r = r2;
        } else {
            int r2 = 1e9;
            while (st.size()) {
                auto itr = st.begin();
                if (itr->first >= r) break;
                tot ++, ans += itr->second;
                int t1 = itr->first, t2 = other[t1];
                if (t2 > r2 or t2 < r) return -1;
                r2 = t2;
                st.erase(itr), st.erase({t2, 0});
            } if (r2 == 1e9) break;
            r = r2;
        } 
    } return min(ans, tot - ans);
}

signed main() {
	iot; file(flower);
    cin >> n;
    rep(i,1,n) {
        cin >> t1 >> t2;
        other[t1] = t2, other[t2] = t1;
        st.insert( { t1, 0 } ), st.insert( { t2, 1 } );
    } while (st.size()) {
        int now = calc();
        if (now == -1) puts("-1"), exit(0);
        ans += now;
    } cout << ans << '\n';
}


\(\text{T4 (fantasy)}\)

幻想中 早已自封盖世英雄 无名无姓有神通 —— 《无理无智

可能在 wlwz 里选这句的原因是这句最平凡?

考虑 DP。对于 DP 状态,我们总应当将一个币值拆分成许多更小的值,以此来生成方案。同时我们应当防止计数重复,因此不妨具体地将使用了几个值设计在状态之内。
我们设 \(f(i, x)\) 用于计数前 \(i\) 种货币凑出 \(m \bmod a_{i + 1} + x\times a_{i + 1}\) 的方案数,其中 \(i < n, x \ge 0\)。可以写出转移:

\[f(i, x) = \sum_{j \ge 0} f\left(i - 1, \frac{m \bmod a_{i + 1}}{a_i} + \frac{x\times a_{i + 1}}{a_i} - j\right) \]

也就是枚举 \(a_i\) 的数量,\(j\) 的上界是 \(f(i - 1, x)\) 的第二维为 \(0\) 时的取值。

可以发现 \(f(i, x)\) 是以 \(x\) 为变量的 \(i - 1\) 次多项式,因为每次往下带入都是带入一个 \(x\) 相关的单项式后求和。因此我们维护 \(f(i, x)\)\(x \in [0, i - 1]\) 的值,每次做前缀和后插值就能得到下一层的一个点值。具体地,对前缀和数组插出 \(\dfrac{m \bmod a_{i + 1}}{a_i} + \dfrac{x\times a_{i + 1}}{a_i}\) 处的值就是 \(f(i, x)\)
最后 \(f(n - 1, m / a_n)\) 就是答案。

总时间复杂度 \(O(n^3)\)。套多项式科技能做到 \(O(n^2 \log^2 n)\) 吗?
有没有突破这个下限的做法?

code
#include <bits/stdc++.h>
using namespace std; 
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
#define multi int T; cin >> T; while ( T -- )
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 100 + 10;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;

// int mod;
const int mod = 1e6 + 3;
// const int mod = 469762049, g = 3;
// const int mod = 998244353; // const int g = 3;
// const int mod = 1004535809, g = 3;
// const int mod = 1e9 + 7;
// const int mod = 1e9 + 9;
// const int mod = 1e9 + 3579, bse = 131;
/* add / sub */ 			template<typename T1,typename T2>T1 add(T1 a,T2 b){return(a+=b)>=mod?a-mod:a;}template<typename T1,typename...Args>T1 add(T1 a,Args...b){return add(a,add(b...));}template<typename T1,typename T2>T1 sub(T1 a,T2 b){return(a-=b)<0?a+mod:a;}template<typename T1,typename...Args>T1 sub(T1 a,Args...b){return sub(a,add(b...));}template<typename T1,typename T2>void addi(T1&a,T2 b){(a+=b)>=mod?(a-=mod):true;}template<typename T1,typename...Args>void addi(T1&a,Args...b){addi(a,add(b...));}template<typename T1,typename T2>void subi(T1&a,T2 b){(a-=b)<0?(a+=mod):true;}template<typename T1,typename...Args>void subi(T1&a,Args...b){subi(a,add(b...));}
/* Fastmod / mul */ 		struct FastMod{int m;ll b;void init(int _m){m=_m;if(m==0)m=1;b=((lll)1<<64)/m;}FastMod(int _m){init(_m);}int operator()(ll a){ll q=((lll)a*b)>>64;a-=q*m;if(a>=m)a-=m;return a;}}Mod(mod);int mul(int a,int b){return Mod(1ll*a*b);}template<typename T1,typename T2>int mul(T1 a,T2 b){return Mod((long long)(1ll*a*b));}template<typename T,typename...Args>int mul(T a,Args...b){return mul(a,mul(b...));}template<typename T1,typename T2>void muli(T1&a,T2 b){a=Mod(1ll*a*b);}template<typename T1,typename...Args>void muli(T1&a,Args...b){muli(a,mul(b...));} // /* trivial multiple function(idk whether its useful*/ int mul(int a, int b) { return 1ll * a * b % mod; } template <typename T1, typename T2> int mul(T1 a, T2 b) { return (long long)(1ll * a * b) % mod; } template <typename T, typename ...Args> int mul(T a, Args ...b) { return mul(a, mul(b...)); }
/* qp fac C */              template<typename T1,typename T2>T1 qp(T1 a,T2 b){T1 ret=1;for(;b>0;a=1ll*a*a%mod,b>>=1)if(b&1)ret=mul(ret,a);return ret;}vi __fac({1,1}),__ifc({1,1}),__inv({0,1});inline void ___prep(int n){static int i=2;if(i<n)for(__fac.resize(n),__ifc.resize(n),__inv.resize(n);i<n;i++)__fac[i]=mul(i,__fac[i-1]),__inv[i]=mul((mod-mod/i),__inv[mod%i]),__ifc[i]=mul(__inv[i],__ifc[i-1]);}inline int fac(int x){return ___prep(x+1),__fac[x];}inline int ifc(int x){return ___prep(x+1),__ifc[x];}inline int inv(int x){return ___prep(x+1),__inv[x];}inline int C(int n,int m){if(n<m or n<0 or m<0)return 0;return mul(fac(n),ifc(m),ifc(n-m));}
/* sum of i^k */            int S(int n,int k){vector<int>__pre(k+4),__suf(k+4),__pw(k+4),__pri(k+4);vector<bool>__vis(k+4,0);__pw[1]=1;for(int i=2,cnt=0;i<=k+2;++i){if(!__vis[i])__pri[++cnt]=i,__pw[i]=qp(i,k);for(int j=1;j<=cnt and i*__pri[j]<=k+2;++j){__vis[i*__pri[j]]=1;__pw[i*__pri[j]]=mul(__pw[i],__pw[__pri[j]]);if(i%__pri[j]==0)break;}}rep(i,2,k+2)__pw[i]=add(__pw[i],__pw[i-1]);__pre[0]=__suf[k+3]=1;rep(i,1,k+2)__pre[i]=mul(__pre[i-1],(n-i+mod));pre(i,k+2,1)__suf[i]=mul(__suf[i+1],(n-i+mod));int tmp=0,ret=0;rep(i,1,k+2){if((k-i)&1)subi(ret,mul(__pw[i],__pre[i-1],__suf[i+1],ifc(i-1),ifc(k+2-i)));else addi(ret,mul(__pw[i],__pre[i-1],__suf[i+1],ifc(i-1),ifc(k+2-i)));}return ret;}
// /* inv 2/3/6 / phi */ 		constexpr int __var_pow(int a, int b) { int ret = 1; for (; b > 0; b >>= 1, a = 1ll * a * a % mod) if (b & 1) ret = 1ll * ret * a % mod; return ret; } constexpr int __var_phi(int x) { if (x <= 2) return 1; int ret = x; for (int i = 2; 1ll * i * i <= x; ++i)  if (x % i == 0) { ret = ret / i * (i - 1); while (x % i == 0) x /= i; } if (x > 1) ret = ret / x * (x - 1); return ret; } const int __phi_mod = __var_phi(mod); const int inv2 = __var_pow(2, __phi_mod - 1), inv3 = __var_pow(3, __phi_mod - 1), inv6 = __var_pow(6, __phi_mod - 1);

int n, ptr, ztr;
ll m, a[N], f[N][N];

ll lagr(int d, ll x, ll y[]) {
    static ll pref[N], suff[N];
    rep(i,0,d) pref[i] = mul(i ? pref[i - 1] : 1, sub(x, i));
    pre(i,d,0) suff[i] = mul(i != d ? suff[i + 1] : 1, sub(x, i));
    ll ret = 0, tmp;
    rep(i,0,d) {
        tmp = y[i];
        if (i) tmp = mul(tmp, ifc(i), pref[i - 1]);
        if (i != d) tmp = mul(tmp, ifc(d - i), d - i & 1 ? mod - 1 : 1, suff[i + 1]);
        addi(ret, tmp);
    } return ret;
}

signed main() {
    file(fantasy);
	cin >> n >> m;
    rep(i,1,n) cin >> a[i];
    if (m % a[1]) puts("0"), exit(0);  
    if (n == 1) puts("1"), exit(0);  
    rep(i,0,n-1) f[1][i] = 1;
    rep(i,2,n-1) {
        rep(j,1,i - 1) addi(f[i - 1][j], f[i - 1][j - 1]);
        rep(j,0,i) f[i][j] = lagr(i - 1, (m % a[i + 1]) / a[i] + j * a[i + 1] / a[i], f[i - 1]);
    } rep(i,1,n-1) addi(f[n - 1][i], f[n - 1][i - 1]);
    cout << lagr(n - 1, m / a[n], f[n - 1]) << '\n';
}
posted @ 2023-01-18 20:25  joke3579  阅读(72)  评论(0编辑  收藏  举报