zoukankan      html  css  js  c++  java
  • CF1436 (Div.2)

    $ ext{A}$

    考虑到对于每一个排列, $frac{a_i}{i}$ 会被算到 $i$ 次,所以这个式子就是 $sumlimits_{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;
    }

    $ ext{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;
    }

    $ ext{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
    ",ans);
        return 0;
    }

    $ ext{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
    ",Max[1]);
        return 0;
    }

    $ ext{E}$

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

    考虑从左往右扫计算答案

    考虑若一个数是一个区间的 $ ext{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
    ",i),0;
        return 0;
    }

    $ ext{F}$

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    3、$x$ 和 $y$ 值不同,贡献是 $x imes y imes [ (|S|-2) imes 2^{|S|-3} + 2^{|S|-2}] imes cnt_x imes cnt_y$ ,问题是我们还是不能枚举 $x$ 和 $y$ /baojin,但是发现对于一种值 $x$ ,它能贡献给其它所有的 $i$ 的倍数,于是记个 $sum=sumlimits_{i mid j} j imes cnt_j$ ,于是每个值 $x$ 的贡献就是 $x imes cnt_x imes (sum - x imes 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
    ",ans);
        return 0;
    }
  • 相关阅读:
    CodeForces 1208 A. Boys and Girls 模拟序列
    CodeForces 1209 B. Jury Size 树状数组处理区间统计问题
    Linux环境进程间通信(转IBM)
    Qt(转IBM)
    POSIX 线程详解(转IBM)
    Perl 编程系列
    Socket in Linux(转IBM)
    Return to the Basic 限定符: const和volatile.
    SQA
    (C++)从本机获取WMI数据.
  • 原文地址:https://www.cnblogs.com/wyzwyz/p/13882784.html
Copyright © 2011-2022 走看看