20220614

  • rk 3/9, 100+30+0=130
  • max: 100,60,100, 100+50+10=160

区间第 k 小

求第 k 大考虑整体二分,需要统计 \(\le mid\) 的个数

出现 \(>w\) 次视为 \(n\) 相当于删掉,对于数值 \(x\),记 \(r\)\(w\) 个权值为 \(1\),第 \(w+1\) 个权值为 \(-w\),把统计个数改为求权值和,容易发现是等价的
求出每个位置对哪些 \([l,r]\) 贡献 \(0/1/w\),问题变为矩形加单点求值,扫描线即可

强制在线就直接存下来整体二分的结构,把用于扫描线的线段树可持久化了,时空复杂度 \(O(n\log^{2}n)\)(其实就是树套树)

赛时做法是用在线化莫队套值域分块,复杂度 \(O(n\sqrt{n})\)

考场代码
const int N = 1e5+5, B = 333, BN = N/B+3;
int n,m,q,ty,ans,a[N],c[N],d[BN],cnt[N],
	in[N],le[BN],ri[BN],bcnt[BN][N],b[BN][BN][BN],bb[BN][BN];
Vi aa;

void add(int *a,int *b,int x) {
	if( a[x]++ == m ) a[n] += m, b[in[n]] += m, b[in[x]] -= m;
	if( a[x] > m ) ++a[n], ++b[in[n]];
	else ++b[in[x]];
}

signed main() { freopen("kth.in","r",stdin); freopen("kth.out","w",stdout);
	io>>n>>m>>q>>ty; For(i,1,n) io>>a[i];
	For(i,0,n) in[i] = i/B; For(i,0,in[n]) le[i] = i*B, ri[i] = le[i]+B-1;
	For(i,0,in[n]) {
		For(j,i,in[n]) {
			if( i < j ) copy(b[i][j-1],b[i][j-1]+in[n]+1,b[i][j]);
			For(k,le[j],ri[j]) add(c,b[i][j],a[k]);
			bb[i][j] = c[n];
		}
		mem(c,0,n);
		if( i ) copy(bcnt[i-1],bcnt[i-1]+n,bcnt[i]);
		For(j,le[i],ri[i]) ++bcnt[i][a[j]];
	}
	while( q-- ) {
		int l,r,ll,rr,k; io>>l>>r>>k; if( ty ) l^=ans, r^=ans, k^=ans;
		ll = in[l]+1, rr = in[r]-1;
		if( ll > rr ) For(i,l,r) aa.pb(a[i]);
		else {
			c[n] = bb[ll][rr], copy(b[ll][rr],b[ll][rr]+in[n]+1,d);
			For(i,l,ri[in[l]]) aa.pb(a[i]);
			For(i,le[in[r]],r) aa.pb(a[i]);
			for(int i : aa) c[i] = bcnt[rr][i]-bcnt[ll-1][i];
		}
		for(int i : aa) add(c,d,i), ++cnt[i];
//		cerr<<"c: "; Rep(i,0,n) cerr<<c[i]<<' '; cerr<<' '<<c[n]<<endl;
		For(i,0,in[n])
			if( k > d[i] ) k -= d[i];
			else
				for(ans = le[i]; ans < n; ++ans) {
					int x = cnt[ans]+(ll<=rr?bcnt[rr][ans]-bcnt[ll-1][ans]:0);
					if( x > m ) continue;
					if( k <= x ) goto togo;
					k -= x;
				}
		togo: io<<ans<<endl;
		for(int i : aa) c[i] = cnt[i] = 0; c[n] = 0;
		mem(d,0,in[n]), aa.clear();
	}
	return 0;
}

求和

推下式子:

\[\sum_{i=1}^{n}\sum_{j=1}^{n}f_{k}(\gcd(i,j))=\sum_{d=1}^{n}f_{k}(d)\left((2\sum_{i=1}^{n/d}\varphi(i))-1)\right) \]

整除分块后杜教筛 \(\varphi\) 前缀和,剩下的问题是求 \(f_k\) 前缀和

PN 筛。构造 \(g=\mu,h(p^{k})=f_{k}(p^{k})+h(p^{k-1})\)

实现细节:预处理每个 PN 的 \(\sum_{i=1}^{k}f_{i}\)线筛前 \(n^{\frac{2}{3}}\)
时间复杂度 \(O(k\sqrt{n}+n^{\frac{2}{3}})\)

code
const int N = 1e7;
LL n;
int m,ans,mnp[N],mx[N],mu1[N],phi1[N],f[N],sf[N];
vector<LL> p;

struct Vec {
	int a[42];
	Vec() { memset(a,0,sizeof a); }
	int& operator [] (int i) { return a[i]; }
	int operator [] (int i) const { return a[i]; }
	void operator += (const Vec &b) { For(i,1,m) a[i] += b[i]; }
	Vec operator * (const Vec &b) const {
		Vec res = *this; For(i,1,m) res[i] *= b[i];
		return res;
	}
	int sum() { return accumulate(a+1,a+m+1,0u); }
} I;

void sieve() {
	bitset<N> vis;
	mx[1] = phi1[1] = mu1[1] = f[1] = 1;
	Rep(i,2,N) {
		if( !vis[i] ) p.pb(i), mnp[i] = mx[i] = 1, phi1[i] = i-1, mu1[i] = f[i] = -1;
		for(int j = 0; j < sz(p) && i*p[j] < N; ++j) {
			vis[i*p[j]] = 1, f[i*p[j]] = -f[i];
			if( !(i % p[j]) ) {
				mnp[i*p[j]] = mnp[i]+1, mx[i*p[j]] = max(mx[i],mnp[i*p[j]]),
				phi1[i*p[j]] = phi1[i] * p[j];
				break;
			}
			mnp[i*p[j]] = 1, mx[i*p[j]] = mx[i],
			phi1[i*p[j]] = phi1[i] * (p[j]-1), mu1[i*p[j]] = -mu1[i];
		}
	}
	Rep(i,1,N) {
		phi1[i] += phi1[i-1], mu1[i] += mu1[i-1], sf[i] = sf[i-1];
        if( mx[i] <= m ) sf[i] += (m-mx[i]+1) * f[i];
    }
}

int mu(LL m) {
	static gp_hash_table<LL,int> mu2;
	if( m < N ) return mu1[m];
	if( mu2.find(m) != mu2.end() ) return mu2[m];
	int res = 1;
	for(LL l = 2, x,r; l <= m; l = r+1)
		x = m/l, r = m/x, res = res - mu(x) * int(r-l+1);
	return mu2[m] = res;
}
int phi(LL m) {
	static gp_hash_table<LL,int> phi2;
	if( m < N ) return phi1[m];
	if( phi2.find(m) != phi2.end() ) return phi2[m];
	int res = int(m) * int(m+1) / 2;
	for(LL l = 2, x,r; l <= m; l = r+1)
		x = m/l, r = m/x, res = res - phi(x) * int(r-l+1);
	return phi2[m] = res;
}

struct {
	Vec hp[34];
	vector<pair<LL,int>> h;
	void dfs(LL n,int i,LL x,Vec hx) {
		LL y = n/x; h.pb(x,hx.sum());
		for(; p[i]*p[i] <= y; ++i)
			for(LL pp = p[i]*p[i], e = 2; pp <= y; pp *= p[i], ++e)
				dfs(n,i+1,x*pp,hx*hp[e]);
	}
	void init() {
		hp[0] = I;
		For(i,1,min(33u,m))
			fill(hp[i].a+i,hp[i].a+m+1,i&1?-1:1), hp[i] += hp[i-1];
		dfs(n,0,1,I), sort(all(h));
	}
	int operator [] (LL n) {
		if( n < N ) return sf[n];
		int res = 0;
		for(auto &i : h) {
			if( i.fi > n ) break;
			res += i.se * mu(n/i.fi);
		}
		return res;
	}
} pn;

signed main() { freopen("sum.in","r",stdin); freopen("sum.out","w",stdout);
	io>>n>>m, sieve();
	For(i,1,m) I[i] = 1;
	pn.init();
	int lst = 0, now;
	for(LL l = 1, x,r; l <= n; l = r+1)
		x = n/l, r = n/x,
		now = pn[r], ans += (now-lst) * (2*phi(x)-1), lst = now;
	io<<(ans&((1<<30)-1));
	return 0;
}

诈骗题。结论:

  1. 答案中每个点不会同时作为起/终点,否则合并起来会少一条路径
  2. 把两条路径 \((u,v),(x,y)\) 改为 \((u,y),(x,v)\) 不影响权值(相交部分一正一反抵消了)

由结论 1 可以初始令每条边为一条路径(从叶子递推到根即可),随意合并,得到每个点作为起/终点的次数。由结论 2 可以贪心地令起/终点各自的字典序最小

code
const int N = 1e6+5;
int n,a[N],f[N];
Vi s,t,e[N];

void dfs(int u,int fa) {
	for(int v : e[u]) if( v != fa ) {
		dfs(v,u), a[u] -= a[v];
		if( u < v ) f[u] += a[v], f[v] -= a[v];
		else f[u] -= a[v], f[v] += a[v];
	}
}

signed main() { freopen("tree.in","r",stdin); freopen("tree.out","w",stdout);
	io>>n; For(i,1,n) io>>a[i]; Rep(i,1,n, x,y) io>>x>>y, e[x].pb(y), e[y].pb(x);
	dfs(1,0);
	For(i,1,n)
		if( f[i] > 0 ) For(j,1,f[i]) s.pb(i);
		else For(j,1,-f[i]) t.pb(i);
	io<<sz(s)<<endl;
	Rep(i,0,sz(s)) io<<s[i]<<' '<<t[i]<<endl;
	return 0;
}
posted @ 2022-06-14 22:11  401rk8  阅读(62)  评论(2编辑  收藏  举报