• Codeforces Round 701 (Div2) 题解(1485A~F)


    Codeforces Round 701 (Div2) 题解

    (zhanglichen 2021.2.13)

    (A.Add and Divider)

    题意:

    您有两个正整数(a)(b)

    您可以执行两种操作:

    (a =⌊a/b⌋)(用a和b之间的除法的整数部分替换a)
    (b = b + 1)(将b增加1)
    找到使(a = 0)所需的最小操作数。

    题解:

    我的做法是枚举(b)的增加次数。这个做法不是很优,但是比较无脑。

    (B.Replace and Keep Sorted)

    题意:

    给出正整数(k),如果满足以下条件,则两个数组称为(k)-相似:

    (1)都严格递增。

    (2)长度相同。

    (3)所有元素都是(1)(k)之间的正整数。

    (4)它们在一个位置上完全不同。

    给出一个严格递增的数组和(q)次查询,每次查询一段区间(l)(r),询问存在多少个数组(b),使得(b)(a[l..r])相似。

    题解:

    依次枚举区间的每个位置,计算当这个位置不同时有多少种(b)

    为了保证数组的单调性,每个位置的取值是有一个上下区间的。对所有区间求和即可。

    注意到除了首尾,每个位置的区间是固定的,所以可以用前缀和预处理。

    时间复杂度(O(n+q))

    (C.Floor and Mod)

    题意:

    如果(⌊a/b⌋= a%b),则一对正整数((a)(b))被称为特殊。

    给出两个整数x和y。 求出特殊对(a,b)的数量,使得(1≤a≤x)(1≤b≤y)

    题解:

    可以转化为(a=k(b+1)),其中(k)小于(b)

    枚举(a)(b)的时间复杂度是无法接受的,一种巧妙的方法是枚举(k)

    时间复杂度(O(sqrt x))

    (D.Mutliples and Power Differences)

    题意:

    给出一个矩阵(a),每个元素小于等于(16)

    请构造一个矩阵(b),使得(b_{i,j})(a_{i,j})的倍数,同时相邻的(b_{i,j})之间的差要是某个正数的4次方。

    题解:

    计算出1到16的公倍数(x),对每个((i+j)\%2=0)的位置赋值(x-a_{i,j}^4),对剩余位置赋值(x)即可。

    之前碰到过一道一模一样的,奈何比赛的时候被(C)暗算了,没开(D),草。

    (E.Move and Swap)

    题意:

    给出一棵树,一开始R和B都在树根1号点。

    每次操作分为以下三步:

    (1)将R移到R所在节点的任意孩子

    (2)将B移到下一层的任意节点(即到1号点的距离比当前位置多1)

    (3)可以交换R和B的位置

    每次移动后,获得(|a_R-a_B|)的收益。

    询问怎么移动可以获得最大收益。

    题解:

    先把节点按照所在层数分组。

    定义(f(i))为当做完上一轮操作后,R在第(i)个点时的最大收益。

    如果知道上一层节点中的每个(f(j)),可以通过(f(j))来计算(f(i))

    可以分为以下两种情况:

    (1)如果在第2步以后,(R)在节点(i),说明(R)的位置没有变,B应当在同一组节点中的最小点或最大点。

    (2)如果在第2步以后,(B)在节点(i)(R)在节点(j(d(1,j)=d(1,i))),那么你必须最大化(d_{father(j)}+|a_j-a_i|)

    这可以对当前层的(a_i)排序,分别计算(a_j leq a_i)(a_i leq a_j)两种情况。

    对当前层的每个(i),最优的(j)不会改变。

    以上是官方题解内容,这题还没理解...

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+100;
    vector<int> g[maxn],d[maxn];
    int dep[maxn],m,n;
    long long f[maxn],a[maxn];
    void dfs (int x,int f) {
    	d[dep[x]].push_back(x);
    	m=max(m,dep[x]);
    	for (int y:g[x]) {
    		if (y==f) continue;
    		dep[y]=dep[x]+1;
    		dfs(y,x);
    	}
    }
    int cmp (int x,int y) {
    	return a[x]<a[y];
    }
    int main () {
    	int _;
    	scanf("%d",&_);
    	while (_--) {
    		scanf("%d",&n);
    		m=0;
    		for (int i=1;i<=n;i++) g[i].clear(),d[i].clear(),f[i]=0,dep[i]=0;
    		for (int i=2;i<=n;i++) {
    			int x;
    			scanf("%d",&x);
    			g[i].push_back(x);
    			g[x].push_back(i);
    		}
    		for (int i=2;i<=n;i++) scanf("%d",a+i);
    		dep[1]=1;
    		dfs(1,0);
    		for (int i=m;i>1;i--) {
    			long long A=-1e18,B=-1e18;
    			sort(d[i].begin(),d[i].end(),cmp);
    			for (int x:d[i]) {
    				A=max(A,f[x]+a[x]);
    				B=max(B,f[x]-a[x]);
    			}
    			for (int x:d[i-1]) {
    				for (int y:g[x]) {
    					if (dep[y]==dep[x]+1) {
    						f[x]=max(f[x],f[y]+abs(a[d[i][0]]-a[y]));
    						f[x]=max(f[x],f[y]+abs(a[d[i].back()]-a[y]));
    						f[x]=max(f[x],A-a[y]);
    						f[x]=max(f[x],B+a[y]);
    					}
    				}
     			}
    		}
    		printf("%lld
    ",f[1]);
    	}
    }
    

    (F.Copy or Prefix Sum)

    题意:

    给出一个数组(b)

    (b_i)可以等于(a_i),也可以等于(sum_{j=1}^ia_j)

    询问有多少个数组(a)

    题解:

    对每个位置(i),你可以选择(a_i=b_i)(a_i=b_i-sum_{k=1}^{i-1}a_k)

    如果(sum_{k=1}^{i-1}a_k=0),那么这两种选择构成的数组是一样的,对答案的贡献是1。

    一种(O(n^2logn))的做法:

    定义(f(i,j))是当前在第(i)位,前缀和是(j)的情况有多少种。

    状态转移方程:

    如果你选择(b_i=a_i),同时(j eq 0)

    (f(i+1,j+b_i)=f(i,j))

    如果你选择(b_i=sum_{k=1}^ia_k)

    (f(i+1,b_i)=f(i,j))

    (Map)实现状态转移方程可以把时间复杂度控制在(O(n^2logn))

    观察状态转移方程,就是把当前位置的所有(j eq 0)(f(i,j))变成(f(i+1,j+b_i)),同时把所有(f(i,j))加给(f(i+1,b_i))

    第二步就是当前的情况数,第一步就是当前的情况数减去(j=0)的情况数。

    合并就是:下一步的情况数=当前的情况数*2-(j=0)的情况数。

    怎么快速计算(j=0)的情况数:

    考虑到每一步,所有状态统一加(b_i),那么第二步可以转化为把所有的(f(i,j))加给(f(i+1,0)),然后把两步的所有情况都变成(f(i+1,j+b_i))

    维护一个懒惰标记,标记当前一共加了多少(即(b)的前缀和)。可以直接用(lazy)表示。

    然后转移的时候,(f(lazy))就表示当前(j=0)的情况,每次转移把(lazy)(b_i)即可。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+100;
    const int mod=1e9+7;
    int b[maxn],n,t;
    map<long long,long long> f;
    int main () {
    	scanf("%d",&t);
    	while (t--) {
    		scanf("%d",&n);
    		for (int i=1;i<=n;i++) scanf("%d",b+i);
    		f.clear();
    		long long lazy=0;
    		long long ans=1;
    		f[0]=1; 
    		for (int i=1;i<=n;i++) {
    			long long tt=ans;
    			ans=(ans*2-f[lazy]+mod)%mod;
    			f[lazy]=tt%mod;
    			lazy-=b[i];
    		}
    		ans%=mod;
    		printf("%lld
    ",ans);
    	}
    }
    
  • 相关阅读:
    php或JS中输出判断项
    拿大神的博客来记一下
    2017.6.8 项目进展
    2017.6.8
    2017.5.18
    2017.5.17
    2017.5.16
    如何实现从php传数据到js
    项目笔记
    tp框架之Model类与命名空间
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/14400270.html
Copyright © 2020-2023  润新知