NOIP模拟71

T1: 签到题

  是的,我签到失败了。。。。。。

  首先有一个结论就是一定存在一种方案,保证度数能被\(c\)整除的点的贡献为\(0\),同时不能被整除的点的贡献为1。

  前置知识:Vizing定理

  证明见这位大佬

Code
#include<bits/stdc++.h>
namespace STD
{
    #define OJ
    #define rr register
    using std::cout;
    using ll=long long;
    const int N=1e6;
    int n,m,k,c;
    int num[N+4];
    int read()
    {
        rr int x_read=0,y_read=1;
        rr char c_read=getchar();
        while(c_read<'0'||c_read>'9')
        {
            if(c_read=='-') y_read=-1;
            c_read=getchar();
        }
        while(c_read<='9'&&c_read>='0')
        {
            x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
            c_read=getchar();
        }
        return x_read*y_read;
    }
};
using namespace STD;
int main()
{
    #ifdef OJ
        FILE * X;
        X=freopen("qiandao.in","r",stdin);
        X=freopen("qiandao.out","w",stdout);
    #endif
    n=read(),m=read();
    k=read(),c=read();
    for(int i=1;i<=k;i++)
    {
        int x=read(),y=read()+n;
        num[x]++,num[y]++;
    }
    ll ans=0;
    for(int i=1;i<=m+n;i++)
        ans+=(num[i]%c!=0);
    cout<<ans<<'\n';
    #ifdef OJ
        fclose(stdin);
        fclose(stdout);
    #endif
}

T2: M 弟娃

  又是一道水题,然而考场上想错了。

  可以发现当\(x,y\)的\(lca\)是除他两个之外的点时,他们只会给各自子树的点贡献答案。

  当\(lca\)为他们其中一个点时,假设是\(x\),那么他们只会贡献给\(S=subtree(y)\cup(tree-subtree(son_x))\)

  其中\(tree\)为整棵树代表的点集,\(son_x\)为\(x\)在链\(x-y\)上的儿子。

  然后线段树维护\(dfn\),支持区间加减与全局最大值即可。

Code
#include<bits/stdc++.h>
namespace Template
{
    template<typename type>
    inline type cmax(type x,type y){return x>y?x:y;}
    #define lc id<<1
    #define rc id<<1|1
    template<int N>
    class sgt
    {
        private:
            struct node
            {
                int maxn;
                int tag;
                node(){maxn=tag=0;}
            };
            node t[N*4+4];
            inline void push_down(int);
        public:
            void insert(int,int,int,int,int,int);
            int query(){return t[1].maxn;}
    };
    template<int N>
    inline void sgt<N>::push_down(int id)
    {
        if(!t[id].tag) return;
        t[lc].tag+=t[id].tag;
        t[rc].tag+=t[id].tag;
        t[lc].maxn+=t[id].tag;
        t[rc].maxn+=t[id].tag;
        t[id].tag=0;
    }
    template<int N>
    void sgt<N>::insert(int id,int l,int r,int st,int en,int val)
    {
        if(st<=l and r<=en)
        {
            t[id].maxn+=val;
            t[id].tag+=val;
            return ;
        }
        int mid=(l+r)>>1;
        push_down(id);
        if(st<=mid) insert(lc,l,mid,st,en,val);
        if(mid<en) insert(rc,mid+1,r,st,en,val);
        t[id].maxn=cmax(t[lc].maxn,t[rc].maxn);
    }
    #undef lc
    #undef rc
};
using namespace Template;
namespace STD
{
    #define OJ
    #define rr register
    using std::cout;
    using ll=long long;
    const int N=3e5;
    int n,m;
    int to[N*2+4],dire[N*2+4],head[N+4];
    int dfn[N+4],size[N+4],fa[N+4],son[N+4],top[N+4];
    int depth[N+4],reid[N+4];sgt<N> t;
    inline void add(int f,int t)
    {
        static int num=1;
        to[++num]=t;
        dire[num]=head[f];
        head[f]=num;
    }
    int read()
    {
        rr int x_read=0,y_read=1;
        rr char c_read=getchar();
        while(c_read<'0'||c_read>'9')
        {
            if(c_read=='-')y_read=-1;
            c_read=getchar();
        }        
        while(c_read<='9'&&c_read>='0')
        {
            x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
            c_read=getchar();
        }
        return x_read*y_read;
    }
    void dfs1(int x=1)
    {
        size[x]=1;
        for(int i=head[x];i;i=dire[i])
        {
            if(to[i]==fa[x]) continue;
            fa[to[i]]=x;
            depth[to[i]]=depth[x]+1;
            dfs1(to[i]);
            size[x]+=size[to[i]];
            if(size[to[i]]>size[son[x]]) son[x]=to[i];
        }
    }
    void dfs2(int x=1)
    {
        static int num=0;
        if(!x)return;
        if(x==son[fa[x]]) top[x]=top[fa[x]];
        else top[x]=x;
        dfn[x]=++num;
        reid[num]=x;
        dfs2(son[x]);
        for(int i=head[x];i;i=dire[i])
        {
            if(to[i]==fa[x] or to[i]==son[x])
                continue;
            dfs2(to[i]);
        }
    }
    int LCA(int x,int y)
    {
        while(top[x]!=top[y])
        {
            if(depth[top[x]]<depth[top[y]])
                x^=y,y^=x,x^=y;
            x=fa[top[x]];
        }
        return depth[x]<depth[y]?x:y;
    }
};
using namespace STD;
int main()
{
    #ifdef OJ
        FILE * X;
        X=freopen("magic.in","r",stdin);
        X=freopen("magic.out","w",stdout);
    #endif
    n=read(),m=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        add(x,y),add(y,x);
    }
    dfs1();dfs2();
    while(m--)
    {
        int x=read(),y=read();
        int lca=LCA(x,y);
        if(lca!=x and lca!=y)
        {
            t.insert(1,1,n,dfn[x],dfn[x]+size[x]-1,1);
            t.insert(1,1,n,dfn[y],dfn[y]+size[y]-1,1);
        }
        else
        {

            if(lca==x)
            {
                t.insert(1,1,n,dfn[y],dfn[y]+size[y]-1,1);
                int last=y;
                while(top[x]!=top[y])
                {
                    last=y;
                    y=fa[top[y]];
                }
                t.insert(1,1,n,1,n,1);
                if(x==y)
                    t.insert(1,1,n,dfn[last],dfn[last]+size[last]-1,-1);
                else
                    t.insert(1,1,n,dfn[x]+1,dfn[x]+size[reid[dfn[x]+1]],-1);
            }   
            else
            {
                t.insert(1,1,n,dfn[x],dfn[x]+size[x]-1,1);
                int last=x;
                while(top[x]!=top[y])
                {
                    last=x;
                    x=fa[top[x]];
                }
                t.insert(1,1,n,1,n,1);
                if(x==y)
                    t.insert(1,1,n,dfn[last],dfn[last]+size[last]-1,-1);
                else
                    t.insert(1,1,n,dfn[y]+1,dfn[y]+size[reid[dfn[y]+1]],-1);
            }
        }
        printf("%d\n",t.query());
    }
    #ifdef OJ
        fclose(stdin);
        fclose(stdout);
    #endif
}

T3: 变异大老鼠

  \(SPT\)(最短路树)\(DP\)。

  头一次见呢。

  先跑一次最短路,几下前驱,然后就可以建出最短路树了。

  最短路树不一定是最小生成树。

  因为最小生成树要求权值和最小,而最短路树里是所有点到定点的最短路,应该还是有区别的。

  然后写个子树合并就可以了。

  \(DP\)方程就是:

temp[j]=cmax(temp[j],temp[j-u]+f[son][u]);

f[x][num]=cmax(f[x][num],temp[j]*(1/son_num[x])*(1-rate[x][num-j]))

  其中\(DP\)定义为在\(x\)的子树中分配\(num\)个警察能抓到老鼠的最大概率。

  第一个式子是合并子树,第二个是枚举在当前点放多少个,来合并出当前子树的答案。

  \(temp\)是合并子树用的容器数组。

  可能是因为写法不太好,跑了2000多,别人都没过1000。

Code
#include<bits/stdc++.h>
namespace Template
{
    template<typename type>
    inline type cmax(type x,type y){return x>y?x:y;}
};
using namespace Template;
namespace STD
{
    #define OJ
    #define rr register
    using std::cout;
    using ll=long long;
    const int M=3e4;
    const int N=300;
    int n,m,k;
    ll dis[N+4];
    bool vis[N+4];
    int to[M*2+4],dire[M*2+4];
    int head[N+4],w[M+4],pre[N+4];
    double rate[N+4][N+4],temp[N+4];
    inline void add(int f,int t)
    {
        static int num=1;
        to[++num]=t;
        dire[num]=head[f];
        head[f]=num;
    }
    int read()
    {
        rr int x_read=0,y_read=1;
        rr char c_read=getchar();
        while(c_read<'0'||c_read>'9')
        {
            if(c_read=='-') y_read=-1;
            c_read=getchar();
        }
        while(c_read<='9'&&c_read>='0')
        {
            x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
            c_read=getchar();
        }
        return x_read*y_read;
    }
    using std::vector;
    vector<int> son[N+4];
    void dijkstra()
    {
        memset(dis,127,sizeof(dis));
        struct pack
        {
            int id;
            ll dis;
            pack(){id=dis=0;}
            pack(int id_,ll dis_)
            {
                id=id_;
                dis=dis_;
            }
            bool operator > (const pack x) const
            {
                return dis>x.dis;
            }
            bool operator < (const pack x) const
            {
                return dis<x.dis;
            }
        };
        std::priority_queue<pack,vector<pack>,std::greater<pack> > heap;
        dis[1]=0;
        heap.emplace(1,0);
        while(heap.size())
        {
            int x=heap.top().id;
            heap.pop();
            if(vis[x]) continue;
            vis[x]=1;
            for(int i=head[x];i;i=dire[i])
                if(dis[to[i]]>dis[x]+w[i>>1])
                {
                    dis[to[i]]=dis[x]+w[i>>1];
                    pre[to[i]]=x;
                    if(!vis[to[i]])
                        heap.emplace(to[i],dis[to[i]]);
                }
        }
    }
    
    double f[N+4][N+4];
    void DP(int x,int num)
    {
        if(f[x][num]!=0.00000 and num)
            return ;
        if(!son[x].size())
        {
            f[x][num]=rate[x][num];
            return;
        }
        for(int i=0;i<son[x].size();i++)
            for(int j=0;j<=num;j++)
                DP(son[x][i],j);
        for(int i=0;i<=num;i++)
            temp[i]=f[son[x][0]][i];
        for(int i=1;i<son[x].size();i++)
            for(int j=num;j;j--)
                for(int u=0;u<=j;u++)
                    temp[j]=cmax(temp[j],temp[j-u]+f[son[x][i]][u]);
        double hh=1.00/(double)son[x].size();
        for(int i=0;i<=num;i++)
            f[x][num]=cmax(f[x][num],temp[i]*hh*(1-rate[x][num-i])+rate[x][num-i]);
    }
};
using namespace STD;
int main()
{
    #ifdef OJ
        FILE * X;
        X=freopen("arrest.in","r",stdin);
        X=freopen("arrest.out","w",stdout);
    #endif
    n=read(),m=read(),k=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        add(x,y),add(y,x);
        w[i]=read();
    }
    dijkstra();
    for(int i=1;i<=n;i++)
        son[pre[i]].emplace_back(i);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=k;j++)
            int ybbb=scanf("%lf",&rate[i][j]);
    DP(1,k);
    printf("%.6lf\n",f[1][k]);
    #ifdef OJ
        fclose(stdin);
        fclose(stdout);
    #endif
}

T4: 朝鲜时蔬

  这题实际上是分类讨论写的式子。

  首先是1-1,显然是\(n----(1)\)

  然后是2-1,式子是

\[\sum_{i=1}^{n}{\lfloor \frac{n}{i}-1 \rfloor}----(2) \]

证明

考虑两个数\(a,b\),假设\(a < b\),有\(a|(a+b)\)(显然不会有\(b|(a+b)\)),那么有\(b=k * a\),枚举\(a\),考虑他的倍数,就有$\lfloor \frac{n}{i} \rfloor \(个,排除他自己,就有\){\lfloor \frac{n}{i}-1 \rfloor}$个

  2-2,式子是\(C_{n}^{2}=n*(n-1)/2\)个,不解释了

  3-1,式子是\(\lfloor \frac{n}{3} \rfloor\)。

证明

考虑三个数\(a,b,c\),令\(a < b < c\),首先考虑最大值是多,肯定是3,因为有例子1,2,3,那么考虑有多少个三元组\((a,b,c)\)满足有\(a|(a+b+c),b|(a+b+c),c|(a+b+c)\),首先,两边除以\((a,b,c)\),就有\(a'|(a'+b'+c'),b'|(a'+b'+c'),c'|(a'+b'+c')\)且\((a',b',c')=1\),满足这样的互质的数只有\(1,2,3\),枚举3的倍数即可。

  3-2,式子是:

\[\sum_{i=1}^{n} \lfloor \frac{n}{i} \rfloor * \frac{i-1-[2|i]}{2} \]

证明

首先假设三元组\((a,b,c)\)满足\(a < b < c\),且\((a+b)|(a+b+c) \ and \ (a+c)|(a+b+c) \ and \ (b+c)|(a+b+c)\)

实际上只会存在\((a+b)|(a+b+c)\),因为\(a< (b+c)\),\(b< (a+c)\)

那么就有\((a+b)|c\),枚举\(a+b\),考虑他的倍数与划分方式即可

考虑计算a1+a2的拆分方案数:枚举a1则可以确定唯一a2,这样“a+b形式”的会被计算2次,“a+a形式”的(如果有)会被计算1次,减掉后者的情况后除以2就能得到a1+a2的拆分方案数。

  其余的见官方题解,不再抄了。

Code


#include<bits/stdc++.h>
namespace Template
{
    template<typename type>
    inline type cmax(type x,type y){return x>y?x:y;}
    template<typename type>
    inline type cmin(type x,type y){return x<y?x:y;}
};
using namespace Template;
namespace STD
{
    #define OJ
    #define rr register
    using std::cout;
    using ll=long long;
    const int mod=1e9+7;
    ll n,m,k;
    ll read()
    {
        rr ll x_read=0,y_read=1;
        rr char c_read=getchar();
        while(c_read<'0'||c_read>'9')
        {
            if(c_read=='-') y_read=-1;
            c_read=getchar();
        }
        while(c_read<='9'&&c_read>='0')
        {
            x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
            c_read=getchar();
        }
        return x_read*y_read;
    }
    ll qpow(ll base,int exp=mod-2)
    {
        ll ret=1LL;
        while(exp)
        {
            if(exp&1) ret=ret*base%mod;
            base=base*base%mod;
            exp>>=1;
        }
        return ret;
    }
    const ll inv2=500000004;
    const ll inv3=333333336;
    const ll inv6=166666668;
    const ll inv9=111111112;
    const ll inv10=700000005;
    const ll inv12=83333334;
    const ll inv15=466666670;
    const ll inv21=47619048;
    const ll inv11=818181824;
    const ll inv29=758620695;
    const ll inv24=41666667;
};
using namespace STD;
int main()
{
    #ifdef OJ
        FILE * X;
        X=freopen("vegetable.in","r",stdin);
        X=freopen("vegetable.out","w",stdout);
    #endif
    n=read(),m=read(),k=read();
    if(m==1 and k==1)
    {
        printf("%lld\n",n%mod);
        return 0;
    }
    if(m==2 and k==1)
    {
        ll l=1,r=0;
        ll ans=0;
        while(r<n)
        {
            r=n/(n/l);
            ans+=((r-l+1)%mod*((n/l)%mod)%mod-(r-l+1)%mod+mod)%mod;
            if(ans>=mod) ans-=mod;
            l=r+1;
        }
        printf("%lld\n",ans);
        return 0;
    }
    if(m==2 and k==2)
    {
        n%=mod;
        printf("%lld\n",n*(n-1)%mod*inv2%mod);
        return 0;
    }
    if(m==3 and k==1)
    {
        n/=3LL;
        printf("%lld\n",n%mod);
        return 0;
    }
    if(m==3 and k==2)
    {
        auto get=
        [](ll l,ll r)->ll
        {
            l--;
            l%=mod,r%=mod;
            ll x1=l*(l+1)%mod*inv2%mod;
            ll x2=r*(r+1)%mod*inv2%mod;
            return (x2-x1+mod)%mod;
        };
        //cout<<get(2,11)<<' '<<get(3,9)<<'\n';
        ll ans=0ll;
        ll l=1,r=0;
        while(r<n)
        {
            r=n/(n/l);
            ans+=(n/l)%mod*((get(l,r)-(r-l+1)%mod+mod)%mod)%mod;
            if(ans>=mod) ans-=mod;
            ans-=(n/l)%mod*((r-2LL*(l/2))/2+(l%2==0))%mod;
            if(ans>=mod) ans-=mod;
            if(ans<0) ans+=mod;
            l=r+1;
        }
        printf("%lld\n",ans*inv2%mod);
        return 0;
    }
    if(m==3 and k==3)
    {
        n%=mod;
        printf("%lld\n",n*(n-1)%mod*(n-2)%mod*inv6%mod);
        return 0;
    }
    if(m==4 and k==1)
    {
        if(n<=5) puts("1");
        else
        {
            ll ans=0LL;
            ans+=n/6;
            if(ans>=mod) ans%=mod;
            ans+=n/9;
            if(ans>=mod) ans%=mod;
            ans+=n/10;
            if(ans>=mod) ans%=mod;
            ans+=n/12;
            if(ans>=mod) ans%=mod;
            ans+=n/15;
            if(ans>=mod) ans%=mod;
            ans+=n/21;
            if(ans>=mod) ans%=mod;
            printf("%lld\n",ans);
        }
        return 0;
    }
    if(m==4 and k==2)
    {
        switch(n)
        {
            case 4LL:
                puts("1");break;
            case 5LL:
                puts("1");break;
            case 6LL:
                puts("1");break;
            case 7LL:
                puts("3");break;
            case 8LL:
                puts("6");break;
            case 9LL:
                puts("9");break;
            case 10LL:
                puts("10");break;
            default:
                n=n/11LL+n/29LL;
                printf("%lld\n",n%mod);
                break;
        };
        return 0;
    }
    if(m==4 and k==3)
    {
        switch(n)
        {
            case 4LL:
                puts("1");
                break;
            case 5LL:
                puts("5");
                break;
            default:
                auto get=
                [](ll l,ll r)->ll
                {
                    l--;
                    l%=mod,r%=mod;
                    ll x1=l*(2*l+1)%mod*(l+1)%mod*inv6%mod;
                    ll x2=r*(2*r+1)%mod*(r+1)%mod*inv6%mod;
                    return (x2-x1+mod)%mod;
                };
                auto get_=
                [](ll l,ll r)->ll
                {
                    l--;
                    l%=mod,r%=mod;
                    ll x1=l*(l+1)%mod*inv2%mod;
                    ll x2=r*(r+1)%mod*inv2%mod;
                    return (x2-x1+mod)%mod;
                };
                ll ans=0LL;
                ll l=1ll,r=0ll;
                while(r<n)
                {
                    r=n/(n/l);
                    ll x=(n/l)%mod;
                    ans+=x*get(l,r)%mod;
                    if(ans>=mod) ans-=mod;
                    ans-=x*6%mod*get_(l,r)%mod;
                    if(ans<0) ans+=mod;
                    ans+=x*5%mod*(r-l+1)%mod;
                    if(ans>=mod) ans-=mod;
                    ans+=x*3%mod*(((r-2*(l/2))/2+(l%2==0))%mod)%mod;
                    if(ans>=mod) ans-=mod;
                    ans+=x*4%mod*(((r-3*(l/3))/3+(l%3==0))%mod)%mod;
                    if(ans>=mod) ans-=mod;
                    l=r+1;
                }
                printf("%lld\n",ans*inv12%mod);
        };
        return 0;
    }
    if(m==4 and k==4)
    {
        n%=mod;
        printf("%lld\n",n*(n-1)%mod*(n-2)%mod*(n-3)%mod*inv24%mod);
        return 0;
    }
    #ifdef OJ
        fclose(stdin);
        fclose(stdout);
    #endif
}
上一篇:王道数据结构第71页,第4题


下一篇:NOIP模拟71