Educational Codeforces Round 83 (Rated for Div. 2)

A,B日常水

C题

https://codeforces.ml/contest/1312/problem/C

Suppose you are performing the following algorithm. There is an array v1,v2,…,vnv1,v2,…,vn filled with zeroes at start. The following operation is applied to the array several times — at ii-th step (00-indexed) you can:

  • either choose position pospos (1≤pos≤n1≤pos≤n) and increase vposvpos by kiki;
  • or not choose any position and skip this step.

You can choose how the algorithm would behave on each step and when to stop it. The question is: can you make array vv equal to the given array aa (vj=ajvj=aj for each jj) after some step?Input

The first line contains one integer TT (1≤T≤10001≤T≤1000) — the number of test cases. Next 2T2T lines contain test cases — two lines per test case.

The first line of each test case contains two integers nn and kk (1≤n≤301≤n≤30, 2≤k≤1002≤k≤100) — the size of arrays vv and aa and value kk used in the algorithm.

The second line contains nn integers a1,a2,…,ana1,a2,…,an (0≤ai≤10160≤ai≤1016) — the array you'd like to achieve.Output

For each test case print YES (case insensitive) if you can achieve the array aa after some step or NO (case insensitive) otherwise.

题目大意:输入T,表示有T组数据;

然后再输入n,k,代表每组数据有n个数,判断这n个数是否能被k^i( i属于[0,无穷])不重复相加所得到 这里不重复代表是i只会从0增加。

比如有两个数 a=k^4+k^5, b=k^3+k^5 这里5 就重复了(具体可以看样例)

我开始还想枚举,有log应该不会超时,可还是超时了?!递归可真是玄学

然后想每个k^i只能拿一次,不是背包吗,i最大有50~60的样子,状压dp?感觉不太行 而且是前三题应该都很水啊

这每个数只能有一个,就是他们k进制&不为1?(好像是这样)

我开始居然忘了k进制怎么写 嘤嘤嘤

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<vector>
#include<cstring>
#include<map>
#include<cstdlib>
#include<queue> 
#include<set>
using namespace std;
const int N=100;
long long n,tot,num,sum[N],a[N],b[N][N],T,maxn,k,flag,x;
int main()
{
    scanf("%lld",&T);
    while(T--)
    {
        maxn=-1;
        flag=0;
        memset(b,0,sizeof(b));
        memset(sum,0,sizeof(sum));
        scanf("%lld %lld",&n,&k);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            tot=0;
            x=a[i];
            while(x)//转为k进制 
            {
                b[i][++tot]=x%k;
                x/=k;
            }
            maxn=max(maxn,tot);
        }
        for(int i=1;i<=maxn;i++)
        {
            for(int j=1;j<=n;j++)
            {
                sum[i]+=b[j][i]; 
                if(sum[i]>=2)
                {
                    flag=1;
                    break;
                }
            }
            if(flag==1)
                break;
        }
        if(flag==1)
            printf("NO\n");
        else
            printf("YES\n");
    }
    return 0;
}

 

D题

https://codeforces.ml/contest/1312/problem/D

Your task is to calculate the number of arrays such that:

  • each array contains nn elements;
  • each element is an integer from 11 to mm;
  • for each array, there is exactly one pair of equal elements;
  • for each array aa, there exists an index ii such that the array is strictly ascending before the ii-th element and strictly descending after it (formally, it means that aj<aj+1aj<aj+1, if j<ij<i, and aj>aj+1aj>aj+1, if j≥ij≥i).

Input

The first line contains two integers nn and mm (2≤n≤m≤2⋅1052≤n≤m≤2⋅105).Output

Print one integer — the number of arrays that meet all of the aforementioned conditions, taken modulo 998244353.

题目大意:输入n,m。有n个空,[1,m]个数字,将这些数字选n-1个再加上选出的数字中的任意一个(一共n个数),进行排序,构造出一个先严格增在严格减的数列,问这种数列有多少种。结果对998244353取模

因为之前做过类似的题(然而还是不会)我很快就想到枚举峰的位置,

感觉题目默认不能只递增和只递减所以位置只能是[2,n-1]

假如峰的位置是i 则左边有i-1个数字要选,右边有n-i个数字要选;

如果先选左边的数字有 Cj-1 i-1种可能,再选右边 有(i-1)*C j-i n-i-1 种可能 因为选出的数字严格增或减所以只有一种排列。因为左边和右边数字不同所以m>=j>(n-i-1+i-1)

枚举一下这样就能得出答案了。但是这样会超时,怎么优化呢

仔细一想,发现左边和右边同时选出来排序也没有影响,相当是先预定位置给峰值而已,最后选出排在峰值左边的数即可,这样表达式就可以简化成 ((C j-1 n-2 )*((i-1) )*(C n-2 i-1)):

峰值位置可以取n-2种,每个位置峰值的值 j属于[n-2,m] 从小于峰值的数中选n-2个数 ,再从 n-2个数中选排在峰值左边的数 又因为可重复一个数( i-1种)从而得到表达式

发现两层循环可以分开求最后乘,然后取mod即可得到答案 (O(n*log?))

代码写的比较冗长....

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<vector>
#include<map>
#include<queue> 
#include<set>
using namespace std;
const int N=2e5+6,mod=998244353;
long long n,m,ans1=0,f[N],ans2=0;
long long pow3(long long a,long long b)
{
    long long ans=1;
    while(b)
    {
        if(b&1)
            ans=ans*a%mod;
        a=a%mod*a%mod;
        b>>=1;
    }
    return ans%mod;
}

int main()
{
    scanf("%lld %lld",&n,&m);
    long long n_2=1;
    for(int i=1;i<=n-2;i++)
    {
        n_2*=i;
        n_2%=mod;
    }
    long long temp1=1,tot1=1,inv_n_2=pow3(n_2,mod-2)%mod;
    for(int i=1;i<=n-3;i++)
    {
        temp1*=i;
        temp1%=mod;
    }
    
    for(int i=n-1;i<=m;i++)
    {
        tot1*=(i-n+1)%mod;
        if(i==n-1)
            tot1=1;
        tot1%=mod;
        temp1*=(i-1)%mod;
        temp1%=mod;
        ans1+=(temp1%mod*pow3(tot1,mod-2)%mod*inv_n_2%mod)%mod;
        ans1%=mod;
    }
    f[n-1]=1;
    for(int i=n-1;i>=2;i--)
    {
        if(i==n-1)
        {
            f[i]=1;
            continue;
        }
        f[i]=f[i+1]*(n-i-1);
        f[i]%=mod;
    }
    long long tot2=1;
    for(int i=2;i<=n-1;i++)
    {

        tot2*=i-1;
        tot2%=mod;
        ans2+=((i-1)*pow3(tot2,mod-2)%mod*pow3(f[i],mod-2)%mod*n_2%mod)%mod;
        ans2%=mod;
    }
    printf("%lld",(ans1%mod*ans2%mod)%mod);
    return 0;
}

 

E题

https://codeforces.ml/contest/1312/problem/E

You are given an array a1,a2,…,ana1,a2,…,an. You can perform the following operation any number of times:

  • Choose a pair of two neighboring equal elements ai=ai+1ai=ai+1 (if there is at least one such pair).
  • Replace them by one element with value ai+1ai+1.

After each such operation, the length of the array will decrease by one (and elements are renumerated accordingly). What is the minimum possible length of the array aa you can get?Input

The first line contains the single integer nn (1≤n≤5001≤n≤500) — the initial length of the array aa.

The second line contains nn integers a1,a2,…,ana1,a2,…,an (1≤ai≤10001≤ai≤1000) — the initial array aa.Output

Print the only integer — the minimum possible length you can get after performing the operation described above any number of times.

题目大意:输入一个n,有n个数字,两个相邻的数字(x)相同的话可以合并成一个新数字 k=x+1,问最后能合并到最短的长度是多少

以前会的区间dp现在居然还没忘

听说是区间dp模板??!

我开始是枚举区间端点,发现这样后面的状态不能从前面的状态推出来,emmm

后来总结了一下像这种没有'顺序'的东西似乎都枚举区间长度??

注意一下i+len要小于n,或者const int N开大一点,我在这re了....

dp i,j是[i,j]区间合并后最小长度,v[i,j] 是合并后的值(如果dp[i,j]!=1,v[i,j]=-1(即不存在))

合并的时候判断一下两个区间的值是否相同

如果相同则dp[i,j]=min(1,dp[i,j]),v[i,j]=(v[i,k] (或者是v[k+1,j] ) +1

如果不同则不能合并 dp[i,j]=min(dp[i,k]+dp[k+1,j],dp[i,j])

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<vector>
#include<map>
#include<queue> 
#include<set>
#include<cstring>
using namespace std;
const int N=1006,inf=0x3f3f3f3f;
int n,a[N],dp[N][N],v[N][N];

int main()
{
    scanf("%d",&n);
    memset(dp,inf,sizeof(dp));
    memset(v,-1,sizeof(v));
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        dp[i][i]=1;
        v[i][i]=a[i];
    }
    for(int len=1;len<=n-1;len++)
    {
        for(int i=1;i<=n-len;i++)//
        {
            int j=i+len;
            for(int k=i;k<j;k++)
            {
                if(v[i][k]==v[k+1][j]&&v[i][k]!=-1)
                {
                    dp[i][j]=min(1,dp[i][j]);
                    v[i][j]=v[i][k]+1;
                }
                else
                {
                    dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
                }
            }
        }
    }
    printf("%d",dp[1][n]);
    return 0;
}

 

F题

https://codeforces.ml/contest/1312/problem/F

看起来是模拟|nim博弈论? 头大

我就先去(学学)Nim博弈

https://www.cnblogs.com/cherrypill/p/12490073.html

emm现在学完了不想填坑了😂

posted @ 2020-03-11 16:37  Sakura_Momoko  阅读(173)  评论(0编辑  收藏  举报