• [Contest on 2022.6.14] 彻底寄了


    \(\cal T_1\) 区间第 \(k\)

    Description

    给定一个长度为 \(n\) 的序列和一个常数 \(w\),所给序列中的任意一个数 \(x\) 满足 \(0 \leqslant x < n\)。现在每次询问 \((l, r, k)\),表示对所给序列询问区间 \([l, r]\) 中第 \(k\) 小的数。需要注意的是,如果某一个数 \(x\) 在这段区间中的出现次数超过了 \(w\),则在求第 \(k\) 小的数时应将区间中所有的数字 \(x\) 视为数字 \(n\).

    \(n,w,q\leqslant 10^5\)强制在线

    Solution

    崩溃,\(\mathcal O(n\log^2n)\) 的正解 \(\mathtt{T}\) 成了赛上写的部分分 ,我真的好难受!

    先讲一下会 \(\mathtt{T}\) 的做法吧:首先,当 \(w=10^5\) 时就是一个裸的树套树,外层做权值线段树,内层做区间线段树,查询时在权值线段树上二分即可。考虑出现次数超过 \(w\) 这个限制不好在询问时处理,所以我们直接预处理 —— 具体就是将权值线段树可持久化,\(rt_r\) 维护询问右端点为 \(r\) 的权值线段树,这样假设 \(a_r=v\),那么先将权值 \(v\) 的区间线段树的下标 \(r\) 加上 \(1\),再找到往左数第 \(w+1\) 个为 \(v\) 的位置 \(p\),将权值 \(v\) 的区间线段树的下标 \(p\) 加上 \(-w-1\),再往 \(p\) 往左数第 \(2\) 个为 \(v\) 的位置 \(p'\),将权值 \(v\) 的区间线段树的下标 \(p'\) 加上 \(w\),就可以成功维护一系列信息。

    我猜想这个做法 \(\mathtt{T}\)\(\rm sb\) 的原因是加入一个数就修改了三次外层线段树,还有就是两层递归常数很大?反正就是 \(\mathtt{T}\) 得我心碎,\(\mathtt{T}\) 得我流泪。

    事实上可以仿照树状数组套权值线段树的写法,预处理出根的数组,再在权值线段树上二分询问。所以考虑将权值线段树放里面,同时为了减少修改外层线段树的次数,可以把单点修改改成区间修改(?),使得属于 \(rt_r\) 区间线段树的 \(l\) 点表示 \([l,r]\) 而不是单点即可,具体就看代码吧。

    Code

    # include <cstdio>
    # include <cctype>
    # define print(x,y) write(x), putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while(!isdigit(s=getchar())) f |= (s=='-');
    	for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
    	return f? -x: x;
    }
    template <class T>
    inline void write(T x) {
    	static int writ[50], w_tp=0;
    	if(x<0) putchar('-'), x=-x;
    	do writ[++w_tp]=x-x/10*10, x/=10; while(x);
    	while(putchar(writ[w_tp--]^48), w_tp);
    }
    
    # include <vector>
    using namespace std;
    
    const int maxn = 1e5+5;
    
    vector <int> vec[maxn];
    int n, W, Q, ty, a[maxn], rk[maxn], rt[maxn], idx, Idx;
    int ls[maxn*200], rs[maxn*200], s[maxn*200];
    int Ls[maxn*600], Rs[maxn*600], ss[maxn*600], Rt[maxn], all;
    
    void ins(int& o,int pre,int l,int r,int p,int k) {
    	Ls[o=++Idx]=Ls[pre], Rs[o]=Rs[pre], ss[o] = ss[pre]+k;
    	if(l==r) return; int mid = l+r>>1;
    	if(p<=mid) ins(Ls[o],Ls[pre],l,mid,p,k);
    	else ins(Rs[o],Rs[pre],mid+1,r,p,k);
    }
    void insert(int& o,int pre,int l,int r,int L,int R,int k,int p) {
    	ls[o=++idx]=ls[pre], rs[o]=rs[pre], s[o]=s[pre];
    	if(l>=L && r<=R) return ins(s[o],s[pre],0,n,p,k), void();
    	int mid = l+r>>1;
    	if(L<=mid) insert(ls[o],ls[pre],l,mid,L,R,k,p);
    	if(R>mid) insert(rs[o],rs[pre],mid+1,r,L,R,k,p);
    }
    void transL() {
    	for(int i=1;i<=all;++i) Rt[i]=Ls[Rt[i]];
    }
    void transR() {
    	for(int i=1;i<=all;++i) Rt[i]=Rs[Rt[i]];
    }
    int query(int l,int r,int k) {
    	if(l==r) return l; int mid = l+r>>1, S=0;
    	for(int i=1;i<=all;++i) S += ss[Ls[Rt[i]]];
    	if(k<=S) return transL(), query(l,mid,k);
    	return transR(), query(mid+1,r,k-S);
    }
    void addRoot(int o,int l,int r,int p) {
    	if(!o) return; Rt[++all]=s[o];
    	if(l==r) return; int mid = l+r>>1;
    	if(p<=mid) addRoot(ls[o],l,mid,p);
    	else addRoot(rs[o],mid+1,r,p);
    }
    
    int main() {
    	freopen("kth.in","r",stdin);
    	freopen("kth.out","w",stdout);
    	n=read(9), W=read(9), Q=read(9), ty=read(9);
    	for(int i=1;i<=n;++i)
    		vec[a[i]=read(9)].emplace_back(i),
    		rk[i] = vec[a[i]].size()-1;
    	for(int i=1;i<=n;++i) { 
    		int las = rk[i]>=W? vec[a[i]][rk[i]-W]: 0;
    		insert(rt[i],rt[i-1],1,n,las+1,i,1,a[i]);
    		if(rk[i]>=W) {
    			int lst = (rk[i]==W? 0: vec[a[i]][rk[i]-W-1]);
    			insert(rt[i],rt[i],1,n,lst+1,las,-W,a[i]);
    		}
    	} int lastans=0;
    	while(Q --) {
    		int l=read(9), r=read(9);
    		l ^= (lastans*ty), r ^= (lastans*ty);
    		int k = read(9)^(lastans*ty); all=0;
    		addRoot(rt[r],1,n,l);
    		print(lastans=query(0,n,k),'\n');
    	}
    	return 0;
    }
    

    \(\cal T_2\) 求和

    Description

    众所周知,当 \(x\) 的质因数分解为 \(∏_i p_i^{a_i}\) 时,有

    \[\mu(x) = \prod_i (−1)^{a_i} [a_i \leqslant 1] \]

    类似地,定义

    \[f_k(x) = \prod_i (−1)^{a_i} [a_i \leqslant k] \]

    现在给定 \(n, k\),求

    \[\sum_{i=1}^{n}\sum_{j=1}^n\sum_{d=1}^k f_d(\gcd(i,j))\pmod {2^{30}} \]

    \(0<n\leqslant 10^{10},0<k\leqslant 40\).

    Solution

    半年未写反演题!老年选手激情推柿一分未得黯然离场

    根据做反演题的经验,上来就是一个枚举 \(\gcd\)

    \[\sum_{r=1}^n\left(\sum_{i=1}^n\sum_{j=1}^n [\gcd(i,j)=r] \right)\cdot \sum_{t=1}^k f_t(r) \]

    中间那一坨是经典莫反,于是答案就变成

    \[\sum_{r=1}^n\left(\sum_{r\mid d} \left\lfloor \frac{n}{d} \right\rfloor^2 \cdot \mu(d/r)\right)\cdot \sum_{t=1}^k f_t(r) \]

    考虑 \(\sum_{r\mid d} \left\lfloor \dfrac{n}{d} \right\rfloor^2 \cdot \mu(d/r)\),枚举 \(d/r\),那么原式等于 \(\sum_{i=1}^{\lfloor n/r \rfloor} \mu(i)\cdot\left\lfloor \dfrac{n}{ir} \right\rfloor^2\),又因为 \(\left\lfloor \dfrac{n}{ir} \right\rfloor^2=\left\lfloor \dfrac{\left\lfloor \frac{n}{r} \right\rfloor}{i} \right\rfloor^2\),证明请戳 \(\rm this\)。为了方便观察,记 \(h(x)=\sum_{i=1}^{ x} \mu(i)\cdot\left\lfloor \dfrac{x}{i} \right\rfloor^2\),那么答案就变成

    \[\sum_{r=1}^n h\left(\lfloor n/r \rfloor\right)\cdot \sum_{t=1}^k f_t(r) \]

    于是可以对 \(h\) 进行整除分块,对于每个 \(\lfloor n/r \rfloor\),还需要再用整除分块算一次函数值,然后用 \(\text{min-25}\) 筛算出对应 \(r\) 区间的 \(f_t\) 前缀和。

    这是过不了的,我们计算 \(h\) 的复杂度实在是太大了。可以将 \(\sum_{i=1}^n\sum_{j=1}^n [\gcd(i,j)=r]\) 表示成一个更为熟知的积性函数[1] —— \(2\cdot \left(\sum_{i=1}^{\lfloor n/r \rfloor} \varphi(i)\right)-1\),考虑枚举 \(\gcd(i,j)=r\) 实际上就是枚举小于等于 \(\lfloor n/r \rfloor\)\(x,y\),满足 \(\gcd(x,y)=1\)。不妨设 \(x>y\),那么枚举 \(x\),对应 \(y\) 的数量为 \(\varphi(x)\)\(y\) 同理,最后需要减去 \((1,1)\).

    \(h(r)=2\cdot \left(\sum_{i=1}^{r} \varphi(i)\right)-1\),于是可以用 \(\text{min-25}\) 筛筛出 \(\varphi(i)\) 的前缀和,容易发现需要的点值都是 \(\lfloor n/r \rfloor\) 的形式,这些在计算 \(h(n)\) 时也会一并被算出,特意记录一下函数值即可。复杂度 \(\mathcal O\left(\frac{kn^{3/4}}{\log n}\right)\).

    Code

    先咕掉。
    

    \(\cal T_3\)

    Description

    给定一棵 \(n\) 个点的树,若按照 \(u\rightarrow v\) 的方向经过边 \((u,v)\)\(u<v\),则会让两个点的点权都增加 \(1\);如果 \(u>v\),则会让两个点的点权都减少 \(1\).

    现在给定每个点最终的点权,尝试构造 \(m\) 条路径 \((x_1,y_1),(x_2,y_2),...,(x_m,y_m)\),使得经过这些路径之后能得到给定的点权。在最小化 \(m\) 的条件下,要求 \(x_1,y_1,x_2,y_2,...,x_m,y_m\) 的字典序最小。

    \(n\leqslant 10^6\),数据保证答案的 \(m\leq n\).

    Solution

    一些闲话:这题不知道怎么搞的,当时竟然觉得网络流 ,结果 \(n=50\) 都不会做(。

    事实上本题的关键在于 这是树形结构。所以先考虑叶子节点,且不考虑路径 只考虑一条边 进行影响,那么它的 \(a\) 可以消除父亲的 \(a\),最后消除到根节点。此时可以发现,每个点的 \(a\) 的绝对值都代表和父亲有多少条匹配边,具体方向由父子编号大小关系决定。

    现在考虑将匹配边合并成路径。显然如果存在 \(u\rightarrow v,v\rightarrow w\),我们可以将其贪心地合并成 \(u\rightarrow w\),而且这一定是最优的。合并之后我们就得到了每个点作为起/终点的次数(容易发现每个点只能作为一种点),事实上,直接将起/终点塞进 \(\rm vector\),各自排序就能得到最优解 —— 这是因为如果有 \(A\rightarrow B,C\rightarrow D\) 的路径,这也相当于 \(A\rightarrow D,B\rightarrow C\) 的路径,因为这是一棵树,两个点之间的简单路径只有一条。于是这相当于起点终点可以任意交换,所以上述做法是正确的。

    Code

    # include <cstdio>
    # include <cctype>
    # define print(x,y) write(x), putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while(!isdigit(s=getchar())) f |= (s=='-');
    	for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
    	return f? -x: x;
    }
    template <class T>
    inline void write(T x) {
    	static int writ[50], w_tp=0;
    	if(x<0) putchar('-'), x=-x;
    	do writ[++w_tp]=x-x/10*10, x/=10; while(x);
    	while(putchar(writ[w_tp--]^48), w_tp);
    }
    
    # include <vector>
    # include <algorithm>
    using namespace std;
    
    const int maxn = 1e6+5;
    
    vector <int> g[maxn], x, y;
    int n, a[maxn], deg[maxn];
    
    int Abs(int x) { return x>0?x:-x; }
    
    void dfs(int u,int fa) {
    	for(const auto& v:g[u]) if(v^fa) {
    		dfs(v,u), a[u] -= a[v];
    		int tmp = (u<v)? a[v]: -a[v];
    		deg[u] += tmp, deg[v] -= tmp;
    	}
    }
    
    int main() {
    	freopen("tree.in","r",stdin);
    	freopen("tree.out","w",stdout);
    	n=read(9);
    	for(int i=1;i<=n;++i) a[i]=read(9);
    	for(int i=1;i<n;++i) {
    		int u=read(9), v=read(9);
    		g[u].emplace_back(v),
    		g[v].emplace_back(u);
    	} dfs(1,0);
    	for(int i=1;i<=n;++i) {
    		int f=deg[i]>0, tmp=Abs(deg[i]);
    		while(tmp) {
    			if(f) x.emplace_back(i);
    			else y.emplace_back(i); --tmp;
    		}
    	}
    	sort(x.begin(),x.end());
    	sort(y.begin(),y.end());
    	print(x.size(),'\n');
    	for(int i=0; i<x.size(); ++i) 
    		printf("%d %d\n",x[i],y[i]); 
    	return 0;
    }
    

    1. 注意 \(h(x)=\sum_{i=1}^{ x} \mu(i)\cdot\left\lfloor \dfrac{x}{i} \right\rfloor^2\) 并不是一个卷积,不要被它的形式诈骗了! ↩︎

  • 相关阅读:
    爱情三十七课,恩情仪式
    爱情三十二课,幽默的用法
    爱情四十二课,距离就是问题
    爱情二十八课,你为什么爱
    爱情三十四课,放手的时机
    爱情三十九课,爱的礼物
    爱情三十三课,读懂愤怒
    爱情三十一课,先信自己
    爱情三十课,爱情整理术
    爱情二十四课,妥协50分
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/16376753.html
Copyright © 2020-2023  润新知