CF1436 (Div.2)

$\text{A}$

考虑到对于每一个排列, $\frac{a_i}{i}$ 会被算到 $i$ 次,所以这个式子就是 $\sum\limits_{i=1}^{n} a_i$ 直接判等不等于 $m$ 即可

$code$ :

#include<cstdio>
#include<cctype>

#define maxn 1001001

inline int read(){
    int r=0,f=0;
    char c;
    while(!isdigit(c=getchar()))f|=(c=='-');
    while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
    return f?-r:r;
}

int n,m;

inline void work(){
    n=read(),m=read();
    long long sum=0;
    for(int i=1;i<=n;i++)
        sum+=read();
    if(sum==1ll*m)puts("YES");
    else puts("NO");
}

int main(){
    int t=read();
    while(t--)work();
    return 0;
}

 

$\text{B}$

首先对于矩阵这个限制很容易解掉,求出一行以后就直接第 $i$ 行从第 $i$ 列输出即可

考虑一行怎么构造,场上不知道 $0$ 也可以,所以就考虑先每个位置填 $1$ 然后第 $n$ 列填哪个合数可以使得和为质数,反正 $n$ 小于等于 $100$ ,乱搞就是了

$code$ :

#include<cstdio>
#include<cctype>

#define maxn 1001001

inline int read(){
    int r=0,f=0;
    char c;
    while(!isdigit(c=getchar()))f|=(c=='-');
    while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
    return f?-r:r;
}

inline bool is_prime(int x){
    if(x<=1)return 0;
    for(int i=2;i*i<=x;i++)
        if(!(x%i))return 0;
    return 1;
}

int n,ans[maxn];

inline void work(){
    n=read();
    for(int i=1;i<=n;i++)ans[i]=1;
    for(int i=0;i<=99999;i++){
        if(is_prime(i+1))continue;
        if(is_prime(n+i)){
            ans[n]+=i;
            break;
        }
    }
    for(int i=1;i<=n;i++,puts(""))
        for(int j=i;j<=n+i-1;j++)
            printf("%d ",ans[(j-1)%n+1]);
}

int main(){
    int t=read();
    while(t--)work();
    return 0;
}

 

$\text{C}$

直接按题目里给的二分模拟,如果 $mid$ 小于给定的 $pos$ ,就可以随便填一个比 $x$ 小的数

如果大于就填一个比 $x$ 大的数,等于就填自己,二分完了剩下的数随便填,乘个阶乘就好了

$code$ :

#include<cstdio>
#include<cctype>

#define maxn 1111

#define mod 1000000007

inline int read(){
    int r=0,f=0;
    char c;
    while(!isdigit(c=getchar()))f|=(c=='-');
    while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
    return f?-r:r;
}

int n,x,pos;

long long ans=1,frac[maxn];

void dfs(int l,int r,int L,int R){
    if(L<0||R<0){
        ans=0;
        return;
    }
    if(l==r){
        (ans*=frac[L+R])%=mod;
        return;
    }
    int mid=(l+r)>>1;
    if(mid<pos)(ans*=L)%=mod,dfs(mid+1,r,L-1,R);
    else if(mid==pos)dfs(mid+1,r,L,R);
    else (ans*=R)%=mod,dfs(l,mid,L,R-1);
}

int main(){
    n=read(),x=read(),pos=read()+1;
    frac[0]=1;
    for(int i=1;i<=n;i++)frac[i]=frac[i-1]*i%mod;
    dfs(1,n+1,x-1,n-x);
    printf("%lld\n",ans);
    return 0;
}

 

$\text{D}$

相当于对于每个点都要分配到子树的叶子节点上,开一个 $Max_u$ 表示 $u$ 节点的叶子节点的最大值,再开个 $sum_u$ 表示 $u$ 子树内所有叶子节点的值与最大值的差距

然后直接分配就好了,如果 $a_u>sum_u$ 就说明 $Max_u$ 要变大,尽量让它均摊下去可以使值最小

$code$ :

#include<cstdio>
#include<cctype>

#define maxn 202202

inline int read(){
    int r=0,f=0;
    char c;
    while(!isdigit(c=getchar()))f|=(c=='-');
    while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
    return f?-r:r;
}

inline long long max(long long a,long long b){
    return a>b?a:b;
}

struct E{
    int v,nxt;
    E() {}
    E(int v,int nxt):v(v),nxt(nxt) {}
}e[maxn];

int n,s_e,head[maxn],a[maxn],size[maxn];

long long Max[maxn],sum[maxn];

inline void a_e(int u,int v){
    e[++s_e]=E(v,head[u]);
    head[u]=s_e;
}

void dfs(int u){
    if(!head[u]){
        size[u]=1;
        Max[u]=a[u];
        return;
    }
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].v;
        dfs(v);
        if(Max[u]<Max[v]){
            sum[u]+=(Max[v]-Max[u])*size[u];
            Max[u]=Max[v];
        }
        else sum[u]+=(Max[u]-Max[v])*size[v];
        sum[u]+=sum[v];
        size[u]+=size[v];
    }
    sum[u]-=a[u];
    if(sum[u]<0){
        long long x=-(sum[u]-size[u]+1)/size[u];
        sum[u]+=x*size[u];
        Max[u]+=x;
    }
}

int main(){
    n=read();
    for(int i=2;i<=n;i++){
        int f=read();
        a_e(f,i);
    }
    for(int i=1;i<=n;i++)a[i]=read();
    dfs(1);
    printf("%lld\n",Max[1]);
    return 0;
}

 

$\text{E}$

感觉这种题没有出过不太可能吧,不过是道好题

考虑从左往右扫计算答案

考虑若一个数是一个区间的 $\text{mex}$ ,肯定这个区间中没有它,所以对于讨论到了 $a_i$ ,就看 $(lst_{a_i},i)$ 这一段区间即可

然后另一个条件就是这个区间中 $x \in [1,a_i-1]$ 都出现过了,似乎不怎么好维护,其实可以转化为 $lst_x > lst_{a_i}$ (就是最后出现的在当前区间的左端点右边)

然后线段树维护一下这个就行了

最后注意一下,扫完以后,对于 $x \in [1,n+1]$ 都要再检查一下 $(lst_x,n]$ 这个区间

$code$ :

#include<cstdio>
#include<cctype>

#define maxn 101101

#define ls (p<<1)
#define rs ((p<<1)|1)

inline int read(){
    int r=0,f=0;
    char c;
    while(!isdigit(c=getchar()))f|=(c=='-');
    while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
    return f?-r:r;
}

inline int min(int a,int b){
    return a<b?a:b;
}

int n,a[maxn],lst[maxn],Min[maxn<<2];

bool app[maxn];

void change(int p,int l,int r,int x,int v){
    if(l==r){
        Min[p]=v;
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid)change(ls,l,mid,x,v);
    else change(rs,mid+1,r,x,v);
    Min[p]=min(Min[ls],Min[rs]);
}

int query(int p,int l,int r,int L,int R){
    if(L<=l&&r<=R)return Min[p];
    int mid=(l+r)>>1;
    if(R<=mid)return query(ls,l,mid,L,R);
    else if(L>mid)return query(rs,mid+1,r,L,R);
    return min(query(ls,l,mid,L,R),query(rs,mid+1,r,L,R));
}

int main(){
    n=read();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++){
        if(a[i]>1&&query(1,1,n,1,a[i]-1)>lst[a[i]])app[a[i]]=1;
        lst[a[i]]=i;
        change(1,1,n,a[i],i);
    }
    for(int i=2;i<=n+1;i++)
        app[i]|=(query(1,1,n,1,i-1)>lst[i]);
    for(int i=1;i<=n;i++)
        app[1]|=(a[i]>1);
    for(int i=1;i<=n+2;i++)
        if(!app[i])return printf("%d\n",i),0;
    return 0;
}

 

$\text{F}$

果然当时应该听 $\text{y}\color{red}{\text{ybyyb}}$ 的话:

 你这种偷懒的莫反啊(指用 $\sum\limits_{d|n} \mu(d) = [n=1]$ ),以后有些题容斥你反演不出来的啊

我当时还觉得这种很牛逼,结果就栽在这道题上了(我这种根本就想不到正解吧 qwq)

设 $g(i)$ 为 $\gcd\{A\}=i$ 的答案,所以最终答案是 $g(1)$

但是由于限制太强了, $g(i)$ 不太好直接求,我们先考虑求点别的

设 $f(i)$ 为 $i \mid \gcd\{A\}$ 的答案,就是 $f(i)=\sum\limits_{i \mid j} g(j)$

所以反演一下就可以知道 $g(i)=\sum\limits_{i \mid j} f(j) \mu(\frac{j}{i})$

于是现在就变成了如何快速求出 $f$ 了,可以首先先枚举 $i$ ,对于每个 $f(i)$ 分别算答案

对于一个 $i$ ,我们可以将题目中那种贡献的拆开来算,首先记 $S$ 为 $x$ 的倍数的可重集,那么就相当于是在 $S$ 里面选 $A$ 集合了(注意 $A$ 也是可重集

由于贡献是 $\sum\limits_{x \in A} x \sum\limits_{y \in B} y$ ,所以我们考虑对于 $S$ 集合中选出的每一对 $x$ 和 $y$ 来分开讨论

1、$x$ 和 $y$ 是同一个元素(不是指同一个值,就是 $x$ 和 $y$ 是 $S$ 中的同一个元素),那么这对 $x$ 和 $y$ 的贡献就是 $x^2 \times (|S|-1) \times 2^{|S|-2}$

  (因为 $x$ 和 $y$ 是同一个元素,如果要能产生贡献,那么一定要在 $B$ 里面,所以对于剩下 $|S|-1$ 个数,它们都可以作为那个在 $A$ 但不在 $B$ 里面的元素,然后还有 $|S|-2$ 个元素,可以随便凑成一个集合,方案数为 $2^{|S|-2}$ )

2、$x$ 和 $y$ 不是同一个元素,那么贡献就是 $x \times y \times [ (|S|-2) \times 2^{|S|-3} + 2^{|S|-2}]$ 中括号里前面的那种和第一种类似,后面那个 $2^{|S|-2}$ 其实就是 $x$ 当那个不在 $B$ 里的数

对于每一对我们知道如何计算了,但是肯定不能直接枚举 $x$ 和 $y$ ,怎么办呢?

我们发现值域小于等于 $10^5$ ,直接枚举 $i$ 的倍数时间复杂度是允许的,就可以考虑对于在 $i$ 的每个倍数之间计算答案

于是有三种选法能对答案造成贡献:

1、$x$ 和 $y$ 值相同,且是同一个元素,那么贡献就是 $x^2 \times (|S|-1) \times 2^{|S|-2} \times cnt_x$ (乘上 $cnt_x$ 是因为等于 $x$ 的值都能这样造成贡献,每个都要算一下)

2、$x$ 和 $y$ 值相同,但不是同一个元素,那么贡献就是 $x^2 \times [ (|S|-2) \times 2^{|S|-3} + 2^{|S|-2}] \times cnt_x \times (cnt_x - 1)$ (后面那个就相当于在 $cnt_x$ 个选 $2$ 个,有顺序

3、$x$ 和 $y$ 值不同,贡献是 $x \times y \times [ (|S|-2) \times 2^{|S|-3} + 2^{|S|-2}] \times cnt_x \times cnt_y$ ,问题是我们还是不能枚举 $x$ 和 $y$ /baojin,但是发现对于一种值 $x$ ,它能贡献给其它所有的 $i$ 的倍数,于是记个 $sum=\sum\limits_{i \mid j} j \times cnt_j$ ,于是每个值 $x$ 的贡献就是 $x \times cnt_x \times (sum - x \times cnt_x)$

然后直接统计答案即可

#include<cstdio>
#include<cctype>

#define maxn 202202
#define mod 998244353

template<class T>

inline T read(){
    T r=0,f=0;
    char c;
    while(!isdigit(c=getchar()))f|=(c=='-');
    while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
    return f?-r:r;
}

inline long long qpow(long long a,int b){
    long long ans=1;
    for(;b;b>>=1){
        if(b&1)(ans*=a)%=mod;
        (a*=a)%=mod;
    }
    return ans;
}

namespace P{

    int n,s_p,p[maxn],mu[maxn];

    bool n_p[maxn];

    inline void init(int N){
        n=N;
        mu[1]=1;
        for(int i=2;i<=N;i++){
            if(!n_p[i])p[++s_p]=i,mu[i]=-1;
            for(int j=1;j<=s_p&&p[j]<=n/i;j++){
                n_p[i*p[j]]=1;
                if(!(i%p[j]))break;
                mu[i*p[j]]=-mu[i];
            }
        }
    }

}

int n,m,cnt[maxn];

long long ans;

int main(){
    m=read<int>();
    for(int i=1;i<=m;i++){
        int x=read<int>();
        cnt[x]=read<int>();
    }
    P::init(n=1e5);
    for(int x=1;x<=n;x++){
        if(!P::mu[x])continue;
        long long tot=0;
        for(int i=x;i<=n;i+=x)tot+=cnt[i];
        if(tot<2)continue;
        long long mult=qpow(2,(tot-2)%(mod-1));
        long long mul1=0,mul2=0;
        mul1=(tot-1)%mod*mult%mod;
        if(tot>2)(mul2+=(tot-2)%mod*qpow(2,(tot-3)%(mod-1))%mod)%=mod;
        (mul2+=mult)%=mod;
        long long f=0,sum=0;
        for(int i=x;i<=n;i+=x){
            (f+=1ll*i*i%mod*cnt[i]%mod*mul1%mod)%=mod;
            if(cnt[i]>1)(f+=1ll*i*i%mod*mul2%mod*cnt[i]%mod*(cnt[i]-1)%mod)%=mod;
            (sum+=1ll*i*cnt[i]%mod)%=mod;
        }
        for(int i=x;i<=n;i+=x)
            (f+=1ll*i*cnt[i]%mod*((sum+mod-1ll*i*cnt[i]%mod)%mod)%mod*mul2%mod)%=mod;
        (ans+=(mod+P::mu[x]*f)%mod)%=mod;
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2020-10-27 08:48  一叶知秋‘  阅读(137)  评论(0编辑  收藏  举报