zoukankan      html  css  js  c++  java
  • 【AtCoder】AtCoder Grand Contest 024 解题报告(A~E)

    点此进入比赛

    (A):Fairness(点此看题面

    • 给定(A,B,C),每次操作令(A=B'+C',B=A'+C',C=A'+B')
    • (K)次操作后(A-B)的值。
    • (A,B,Cle10^9,Kle10^{18})

    签到题

    考虑同时将(A,B,C)减去一个数并不会影响答案。

    一次操作后(A=B'+C',B=A'+C',C=A'+B'),故(A-B=B'-A')

    两次操作后(A=2A'+B'+C',B=2B'+A'+C',C=2C'+A'+B'),同时减去(A'+B'+C')得到(A=A',B=B',C=C')

    所以只要判断(K)的奇偶性即可。

    代码:(O(1))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    using namespace std;
    int A,B,C;long long k;
    int main()
    {
    	return scanf("%d%d%d%lld",&A,&B,&C,&k),printf("%d
    ",(k&1)?B-A:A-B),0;//判断k的奇偶性输出答案
    }
    

    (B):Backfront(点此看题面

    • 给定一个长度为(n)的排列,每次操作你可以将一个数移到序列开头或结尾。
    • 问最少操作多少次使得整个序列有序。
    • (nle2 imes10^5)

    最长连续上升子序列

    显然的结论,我们可以直接把要操作的数从序列中删去,因为它们能够以任意的顺序随意放在开头或结尾。

    那么我们只要使得序列中剩余的数满足条件,这必然是一个连续上升的序列(注意这里的连续指的是数的值)。

    于是问题就转化为求原序列的最长连续上升子序列,应该是很好做的。

    代码:(O(n))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 200000
    using namespace std;
    int n,f[N+5];
    int main()
    {
    	RI i,x,t=0;for(scanf("%d",&n),i=1;i<=n;++i)
    		scanf("%d",&x),t=max(t,f[x]=f[x-1]+1);return printf("%d
    ",n-t),0;//每个数从比它小1的数转移
    }
    

    (C):Sequence Growing Easy(点此看题面

    • 给定一个长度为(n)的序列(A),你有一个初始全为(0)的序列(X)
    • 每次操作你可以任选一个(i)(X_{i+1}=X_i+1)
    • 问最少操作多少次使得(X)变成(A)
    • (nle2 imes10^5,Ale10^9)

    结论

    先考虑判无解,如果(A_1>0)(exists iin[2,n],A_i>A_{i-1}+1),就无解。

    否则,若(A_i=A_{i-1}+1),显然我们只要一次操作就可以直接从(A_{i-1})得到(A_i)

    不然,只能操作(A_i)次专门生成(A_i)

    这个可以自行举几个例子画画图,应该是很好理解的。

    代码:(O(n))

     #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 200000
    using namespace std;
    int n,a[N+5];long long ans;
    int main()
    {
    	RI i;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",a+i);
    	if(a[1]) return puts("-1"),0;for(i=2;i<=n;++i) if(a[i]>a[i-1]+1) return puts("-1"),0;//判无解
    	for(i=2;i<=n;++i) a[i]==a[i-1]+1?++ans:(ans+=a[i]);return printf("%lld
    ",ans),0;//分两类计算答案
    }
    

    (D):Isomorphism Freak(点此看题面

    • 对于一棵无根树,若以某两点为根得到的有根树同构,就给这两点染上相同的颜色。
    • 给定一棵(n)个点的无根树,你可以任意添加节点。
    • 问最终得到的树至少需要染多少种颜色,且在此前提下最少有几个叶节点。
    • (nle100)

    枚举中心

    考虑我们对这棵树枚举一个中心,可以是点,也可以是边。

    当选中某一个中心后,最少的颜色数就是最远的叶节点的深度

    为实现这个颜色数,我们需要把这棵树填满,也就是说,对于任意一层的节点,它们的叶节点数都相同

    因此只要统计下每一层叶节点数的最大值,然后乘起来就是答案。

    代码:(O(n^2))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    #define LL long long
    using namespace std;
    int n,ee,lnk[N+5],Mx[N+5];struct line {int x,y;}l[N+5];
    struct edge {int to,nxt;}e[N<<1];
    I int Work(CI x,CI lst=0)//求最远的叶节点深度
    {
    	RI t=0;for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&(t=max(t,Work(e[i].to,x)));return t+1;
    }
    I void dfs(CI x,CI lst=0,CI d=1)//统计每层叶节点数最大值
    {
    	RI t=0;for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&(dfs(e[i].to,x,d+1),++t);Mx[d]=max(Mx[d],t);
    }
    I LL Calc(CI x,CI y=0)//计算叶节点数
    {
    	RI i;LL t=1;for(i=1;i<=n;++i) Mx[i]=0;dfs(x,y),y&&(dfs(y,x),0);
    	for(i=1;i<=n&&Mx[i];++i) t*=Mx[i];return t*(y?2:1);//求乘积,如果以边为中心还要额外乘2
    }
    int main()
    {
    	RI i;for(scanf("%d",&n),i=1;i^n;++i)
    		scanf("%d%d",&l[i].x,&l[i].y),add(l[i].x,l[i].y),add(l[i].y,l[i].x);
    	RI s=n;for(i=1;i<=n;++i) s=min(s,Work(i));//以点为中心
    	for(i=1;i^n;++i) s=min(s,max(Work(l[i].x,l[i].y),Work(l[i].y,l[i].x)));//以边为中心
    	LL g=1LL<<60;for(i=1;i<=n;++i) Work(i)==s&&(g=min(g,Calc(i)));//以点为中心
    	for(i=1;i^n;++i) (max(Work(l[i].x,l[i].y),Work(l[i].y,l[i].x)))==s&&(g=min(g,Calc(l[i].x,l[i].y)));//以边为中心
    	return printf("%d %lld
    ",s,g),0;//输出答案
    }
    

    (E):Sequence Growing Hard(点此看题面

    • (n+1)个值域为([1,k])的序列(A_0,A_1,...,A_n),问有多少种可能的序列组满足下列条件:
      • (A_0)为空,其余(A_i)是由(A_{i-1})插入一个元素得到的。
      • (forall iin[1,n],A_{i})的字典序大于(A_{i-1})
    • (n,kle300)

    奇怪的(DP)

    一个很套路的思路,就是从小到大插入数,这样明显随便插入都可以满足字典序的要求。

    但是,为了避免计算重复,我们必须强制对于相同的数,新插入的数必须放在最后面。

    因此设(f_{i,j,k})表示进行到第(i)个操作,放到数字(j),有(k)个数之后可以放(加上开头,共有(k+1)个位置可以放)。

    转移分三种:(刷表)

    1. 这个位置后不放数:(f_{i,j,k-1} exttt{+=}f_{i,j,k}(k>0))
    2. 重新开始放下一个数字:(f_{i,j+1,i} exttt{+=}f_{i,j,k}(k=0))
    3. 放这个数:(f_{i+1,j,k} exttt{+=}f_{i,j,k} imes(k+1))

    代码:(O(n^3))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 300
    #define Inc(x,y) ((x+=(y))>=X&&(x-=X))
    using namespace std;
    int n,k,X,f[N+5][N+5][N+5];
    int main()
    {
    	RI i,j,x;scanf("%d%d%d",&n,&k,&X),f[0][1][0]=1;//初始化
    	for(i=0;i<=n;++i) for(j=1;j<=k;++j) for(x=i;~x;--x)//DP
    		x?Inc(f[i][j][x-1],f[i][j][x]):Inc(f[i][j+1][i],f[i][j][x]),//前两种转移
    		f[i+1][j][x]=(1LL*(x+1)*f[i][j][x]+f[i+1][j][x])%X;//第三种转移
    	return printf("%d
    ",f[n][k][0]),0;//输出答案
    }
    
  • 相关阅读:
    在Visual Studio中怎样快速添加代码段
    18个不常见的C#关键字,您使用过几个?
    C# 非常好用的组元Tuple
    C# List根据另一个List集合或数组排序
    Expression 核心操作符、表达式、操作方法
    如何避免频繁创建临时对象
    C# 23种设计模式
    C# 23种设计模式
    Api Cloud官方日期类型转换
    sql server 保留小数(续A)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/AtCoderAGC024.html
Copyright © 2011-2022 走看看