20210713考试-2021noip13

这位巨佬的博客还是比我好多了


 T1 工业题

考场:

暴力挺香的,不想正解了。

题解:

$f(i,j)$ 只会得到 $f(i-1,j)$ 和 $f(i,j-1)$ 的贡献。每向右一步乘 $a$ ,向下一步乘 $b$ 。左侧竖点有$\binom{n+m-1-i}{m-1}$种走法,上侧横店有$\binom{n+m-1-i}{n-1}$种走法.

预处理阶乘和阶乘逆元以及 $a,b$ 的幂次方即可。

#include<bits/stdc++.h>
using namespace std;
const int N=11000000,mod=998244353;
long long a,b,n,m,inc[N],jc[N],am[N],bm[N],fheng[N],fshu[N],ans;
long long quick_pow(long long x,long long y)
{
    long long res=1;
    while(y)
    {
        if(y&1)
        {
            res=res*x%mod;
        }
        y>>=1;
        x=x*x%mod;
    }
    return res;
}
long long C(int n,int m)
{
    return jc[n]*inc[m]%mod*inc[n-m]%mod;
}
int main()
{
    scanf("%lld%lld%lld%lld",&n,&m,&a,&b);
    a%=mod;b%=mod;
    am[0]=bm[0]=jc[0]=inc[0]=1;
    for(int i=1;i<=n+m;i++)
    {
        jc[i]=jc[i-1]*i%mod;
        am[i]=am[i-1]*a%mod;
        bm[i]=bm[i-1]*b%mod;
    }
    inc[n+m]=quick_pow(jc[n+m],mod-2);
    for(int i=n+m-1;i>=1;i--)
    {
        inc[i]=inc[i+1]*(i+1)%mod;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&fheng[i]);fheng[i]%=mod;
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%lld",&fshu[i]);fshu[i]%=mod;
    }
    for(int i=1;i<=n;i++)
    {
        ans+=fheng[i]*C(n+m-1-i,m-1)%mod*am[m]%mod*bm[n-i]%mod;
        ans%=mod;
    }
    for(int i=1;i<=m;i++)
    {
        ans+=fshu[i]*C(n+m-1-i,n-1)%mod*am[m-i]%mod*bm[n]%mod;
        ans%=mod;
    }
    printf("%lld\n",ans);
    return 0;
}

 


 

 T2 卡常题

 考场:

灵光乍现,发现如果把y部点删去,只剩下每个y点所连的边,题目所给的图就变成了一棵基环树。

原因:x,y部点都只有 $n$ 个如果把y点变成 $n$ 条边,那么由于题目保证联通且无重边,$n-1$ 的边构成一棵树,剩下的一条边随机连接,构成环。

问题就转化成了,在一棵基环树上,选择一个点的代价为该点所连边的权值和,选择的点可以“看守”所连的边,求所有边被看守的最小代价。

题解:

考场上我按照轮流断掉环中的每一条边,每次跑 $O(n)$ 树形DP来做的,即使看到能被卡成 $O(n^2)$ 的环形数据,依然执迷不悟。

事实上断哪一条边对答案完全没有影响,因为DP所求的和所需的完全没有变。 而且如果随机选一个点作为树根,难以保证断掉的边两侧的点一定选择了之一。

所以钦定其中一个点为根,跑两次DP。

#include<bits/stdc++.h>
using namespace std;
const int N=1100000,M=N<<1;
int n,aa,bb,cnt=1,head[N],circle[N<<1],num,c1,c2,du[N],e,f[2][N][2],ans=0x7fffffff,sam[N];
bool vis[N],used[N];
inline int read()
{
    int s=0,w=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
    return s*w;
}
inline int _min(int a,int b)
{
    return a<b?a:b;
}
struct bian
{
    int nxt,to,w[2];
}b[M];
void add(int from,int to,bool g)
{
    du[from]++;du[to]++;
    b[++cnt].w[g]=aa;
    b[cnt].w[g^1]=bb;
    b[cnt].nxt=head[from];
    b[cnt].to=to;
    head[from]=cnt;
}
void findcir1(int u,int fa)
{
    vis[u]=1; 
    for(int i=head[u],v;i;i=b[i].nxt)
    {
        v=b[i].to;
        if(v==fa) continue;
        else if(vis[v])
        {
            c1=u,c2=v;e=i;
            return;
        }
        else
        {
            findcir1(v,u);
        }
    }
}
bool findcir2(int u)
{
    bool ok=0;
    for(int i=head[u],v;i;i=b[i].nxt)
    {
        if(used[i])
            continue;
        v=b[i].to;
        circle[++num]=i;
        circle[++num]=i^1;
        used[i]=used[i^1]=1;
        if(du[v]==2)
        {
            circle[num--]=0;
            circle[num--]=0;
            continue;
        }
        if(v==c1)
        {
            ok=1;return ok;
        }
        ok=findcir2(v);
        if(!ok)
        {
            circle[num--]=0;
            circle[num--]=0;
            continue;
        }
        else
            return ok;
    }
    return ok;
}
void DP(int x,int fa,int o)
{
    int sum=0;
    f[o][x][0]=0;
    f[o][x][1]=sam[x];
    for(int i=head[x];i;i=b[i].nxt)
    {
        if(i==e||(i^1)==e||b[i].to==fa)continue;
        DP(b[i].to,x,o);
        sum+=min(f[o][b[i].to][0],f[o][b[i].to][1]);
        f[o][x][0]+=f[o][b[i].to][1];
    }
    f[o][x][1]+=sum;
}
int main()
{
    n=read();aa=read();bb=read();
    for(int i=1,u,v;i<=n;i++)
    {
        u=read();v=read();
        add(u,v,0);
        add(v,u,1);
        sam[u]+=aa;
        sam[v]+=bb;
    }
    findcir1(1,0);
    findcir2(c1);
    memset(used,0,sizeof(used));
    DP(c1,0,0);
    DP(c2,0,1);
    printf("%d\n",_min(f[0][c1][1],f[1][c2][1]));
    return 0;
}

 


 

T3 玄学题 

 

 

$\begin{align}ans=\sum_{i=1}^{n}(-1)^{\sum_{j=1}^{m}d(i*j)}\\ans=\sum_{i=1}^{n}(-1)^{\sum_{j=1}^{m}d(i*j) \bmod 2}\end{align}$

因为奇+奇=偶,奇+偶=偶,偶+偶=偶,

所以,只有 $d(i*j)$ 为奇数,才对和式整体有贡献。

这时 $i*j$ 为完全平方数,假设i=p*q*q,那么 $j$ 是 $p*r*r$。

对于每一个 $i$,有$\sqrt{\lfloor \frac{m}{p} \rfloor}$ 个j对和式产生了贡献。

(只有能使 $-1$ 的幂增减 $1$ 的才算对和式产生了贡献)

#include<bits/stdc++.h>
using namespace std;
inline long long read()
{
    long long s=0,w=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
    return s*w;
}
const int N=11000000;
long long n,m,ans,p[N];
bool flag[N];
int main()
{
    n=read();m=read();
    scanf("%lld%lld",&n,&m);
    for(int i=1;i*i<=n;i++)
    {
        for(int j=1;j*i*i<=n;j++)
        {
            p[i*i*j]=j;
        }
        flag[i*i]=1;
    }
    int s=sqrt(m);
    for(int i=1;i<=n;i++)
    {
        if(flag[i])
        {
            if(s&1)
                ans--;
            else
                ans++;    
        }
        else
        {
            int x=sqrt(m/p[i]);
            if(x&1)
                ans--;
            else ans++;    
        }
    }
    printf("%lld\n",ans)
    ;
    return 0; 
} 

 这两场都好拉跨啊QWQ每一次就离正解一步之遥。

 

posted @ 2021-07-13 16:24  HKHbest  阅读(30)  评论(0编辑  收藏  举报
Live2D