• AtCoder Regular Contest 123


    D.Inc,Dec-Decomposition

    题目描述

    点此看题

    给一个长度为 (n)(A) 序列,试构造单调不降的 (B) 和单调不增的 (C),满足 (a_i=b_i+c_i) 并且最小化代价:

    [sum_{i=1}^n|b_i|+|c_i| ]

    (nleq 2cdot 10^5,-10^8leq a_ileq 10^8)

    解法

    这题 ( t slope trick) 做不带脑子,还是先把前置知识看一下吧。

    首先写出暴力 (dp),管它多大的数直接塞状态里,设 (dp[i][j]) 表示考虑到 (i),并且 (b_i=j) 的最小代价,转移:

    [dp[i][j]=dp[i-1][k]+|j|+|a_i-j| kleq j,a_{i-1}-kgeq a_i-j ]

    发现新增的代价是绝对值的形式,可以看成折线函数直接合并上去,然后考虑限制 (kleq j) 是一个取前缀最小值的操作,但是 (k-(a_{i-1}-a_i)leq j) 就不是单纯的前缀最小值了,但是可以把 (k) 先往右平移 (a_i-a_{i-1}),然后再取前缀最小值,直接打整体标记就行了。

    时间复杂度 (O(nlog n)),折线算法总是超级超级短(sim)

    总结

    ( t slope trick) 可以优化 (dp),直接写暴力方程式然后观察代价与限制即可,打整体标记是常用方法。

    真正在做题的时候先考虑操作的顺序,比如这题必须先插入两条绝对值折线之后再取最小值。再讨论插入点和 (0) 斜率线的关系,取的最优决策点应该既在原来的 (0) 斜率线上又在当前的 (0) 斜率线上,本题可以通过讨论证明一定存在这样的点。

    (2021/10/8) 补充:注意每个位置实际上会有两个重合的转移点。

    #include <cstdio>
    #include <queue>
    using namespace std;
    const int M = 200005;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,sh,ans,a[M];priority_queue<int> q;
    signed main()
    {
    	n=read();
    	for(int i=1;i<=n;i++)
    	{
    		a[i]=read();int x=a[i],y=0;
    		if(i>1) sh+=max(a[i]-a[i-1],0ll);//tag
    		q.push(-sh);q.push(-sh);//insert the change point
    		q.push(a[i]-sh);q.push(a[i]-sh);
    		q.pop();y=q.top()+sh;q.pop();//pre min
    		ans+=2*y-x;//update the 0-slope-line
    	}
    	printf("%lld
    ",ans);
    }
    

    F.Insert Addition

    题目描述

    点此看题

    一个序列 (p),定义生成序列 (f(p)) 为把 (p_i+p_{i+1}) 插入到 (i,i+1) 中间生成的序列。

    初始时 (A=(a,b)),我们把 (A) 替换成 (f(A)) 进行 (n) 次,然后得到删除所有 (>n) 的数,得到序列 (B),求 (B_L...B_R) 具体的值。

    (a,bleq nleq 3cdot 10^5,Lleq Rleq 10^{18},R-L<3cdot 10^5)

    前言

    首先我想用贡献法直接求出 (n) 次后 (B) 某个位置的值,发现根本就不会,因为这题是拿来给你找性质的。

    你最好先看一下 Stern-Brocot树,本题的很多性质都可以通过类比它获得(所以说学算法要看证明)。

    解法

    首先声明 (a,b) 不是很重要,因为我们可以把原数写成系数对的形式,比如这样:

    [egin{matrix} (1,0)&(0,1)\ (1,0)&(1,1)&(0,1)\ (1,0)&(2,1)&(1,1)&(1,2)&(0,1)\ (1,0)&(3,1)&(2,1)&(3,2)&(1,1)&(2,3)&(1,2)&(1,3)&(0,1) end{matrix} ]

    我继续声明 (n) 不是很重要,因为生成新数的大小永远大于当前迭代次数,所以 (n) 的增大只是再 (B) 序列中激活了更多旧数,那么我们可以考虑充分多次迭代后保留 (leq n) 的数,这和原问题是等价的。

    结合你对系数对的观察和类比 ( t Stern-Brocot) 树,我们开始找性质。

    性质一

    如果 ((x_1,y_1),(x_2,y_2)) 在系数数列中相邻,那么 (x_1y_2-x_2y_1=1)

    证明考虑归纳法,首先第一行是成立的,我们考虑 (x_3=x_1+x_2,y_3=y_1+y_2) 时,((x_1,y_1),(x_3,y_3)) 有没有此性质:

    [x_1y_3-x_3y_1=x_1(y_1+y_2)-(x_1+x_2)y_1=x_1y_2-x_2y_1=1 ]

    对于 ((x_3,y_3),(x_1,y_1)) 也可类似证得,所以可以推至所有情况。

    性质二

    如果 (x_1y_2-x_2y_1=1) 并且 (0leq x_1,y_1,x_2,y_2),那么 ((x_1,y_1))((x_2,y_2)) 在某时刻的系数序列中会相邻。

    ((1,0))((0,1)) 开始归纳,我们逐步增大这些系数对的值,分为下面两种情况归纳:

    • (x_1leq x_2,y_1leq y_2)
    • (x_1geq x_2,y_1geq y_2)

    证明第一种情况可以归纳,设 ((x_3,y_3)=(x_2-x_1,y_2-y_1)),由先前的假设可知 (x_1y_3-x_3y_1=1),那么生成的 ((x_2,y_2)) 就满足 (x_1y_2-x_2y_1=1),并且它们在系数数列中相邻,另一种情况类似证明即可。

    推论

    如果 (x,y) 满足 (gcd (x,y)=1),那么 ((x,y)) 会出现在充分多次迭代后的系数数列,且系数数列只包含这样的 ((x,y))

    考虑方程 (xx'-yy'=1),当且仅当满足 (gcd(x,y)=1) 时有解 ((x',y')),根据性质二可以知道 ((x,y)) 存在的充要条件是 (gcd(x,y)=1),并且由归纳法可知 ((x,y)) 只会出现一次。


    查询可以当成在一棵二叉树上跑,根据线段树的复杂度可知跑的次数是 (O(log L))

    那么需要解决的问题是查询某个点子树内大小 (leq n) 数的个数,树上的点可以用生成它的两个端点 ((a,b)) 表示(注意此时我们用整数而不用系数对了),也就是求满足 (icdot a+jcdot bleq n) 并且 (gcd(i,j)=1) 的点对 ((i,j)) 的个数。

    为了解决 (gcd(i,j)=1),显然可以莫比乌斯反演,设 (f(n)) 表示满足上述条件的点对个数,(F(n)) 则为不考虑 (gcd(i,j)=1) 的点对个数,那么 (f(n)=sum_{i}mu(i)F(frac{n}{i})),可以暴力枚举 (i) 然后暴力算 (F(i)),时间复杂度 (O(nlog n))

    如果遇到一个点的子树都在 ([L,R]) 的范围中就直接访问这个子树把答案输出,是均摊 (O(n)) 的,总时间复杂度 (O(nlog nlog L)),官方题解在这一部分的做法根本看不懂,所以不要去管他。

    总结

    分析性质的重要方法是去除无关量,比如这题把 (n,a,b) 全部都不管,问题就在充分迭代背景下发生了。

    归纳法需要从初始状态找性质,然后尝试推广到以后的状态,它很适用于充分迭代的问题,类比学过的结构也是找性质的重要方法。

    总所周知 ( t stern-brocot) 树可以看成一棵线段树,这题就是很好的例子。

    #include <cstdio>
    #define int long long
    const int M = 300005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,a,b,L,R,cnt,p[M],mu[M],vis[M];
    void init(int n)
    {
    	mu[1]=1;
    	for(int i=2;i<=n;i++)
    	{
    		if(!vis[i]) p[++cnt]=i,mu[i]=-1;
    		for(int j=1;j<=cnt && i*p[j]<=n;j++)
    		{
    			vis[i*p[j]]=1;
    			if(i%p[j]==0) break;
    			mu[i*p[j]]=-mu[i];
    		}
    	}
    }
    void print(int x)
    {
    	if(L>R || x>n) return ;
    	if(L==1) printf("%lld ",x),R--;
    	else L--,R--;
    }
    void pall(int x,int y)
    {
    	if(x+y>n) return ;
    	pall(x,x+y);print(x+y);pall(x+y,y);
    }
    int cal(int p,int q)
    {
    	int r=0;
    	for(int i=1;p+q<=n/i;i++)
    	{
    		if(!mu[i]) continue;
    		for(int j=1;p*j+q<=n/i;j++)
    			r+=mu[i]*(n/i-p*j)/q;
    	}
    	return r;
    }
    void dfs(int a,int b)
    {
    	int m=cal(a,b);
    	if(L>R || !m) return ;
    	if(L>m) {L-=m;R-=m;return ;}
    	if(L==1 && m<=R) {pall(a,b);return ;}
    	dfs(a,a+b);print(a+b);dfs(a+b,b);
    }
    signed main()
    {
    	a=read();b=read();n=read();L=read();R=read();
    	init(n);
    	print(a);dfs(a,b);print(b);
    }
    
  • 相关阅读:
    Java的Class类及static块的执行时机
    Java中Scanner用法总结
    JavaWeb中表单数据的获取及乱码问题
    JavaWeb_MVC 设计模式
    JavaScript高级程序设计(读书笔记)之函数表达式
    JavaScript高级程序设计(读书笔记)之BOM
    JavaScript高级程序设计读书笔记之JSON
    WPF 引用 ttf文件
    WPF Binding Mode,UpdateSourceTrigger
    WPF 选项卡
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15035706.html
Copyright © 2020-2023  润新知