计蒜课 八月模拟赛题解

看见机房有大佬上周写了上面的普及信心赛 于是我康了康 8月的提高组模拟赛 9月的还没开始qwq

真的 有点难 主要是我先打开了T2 我再次 对自己的数学产生了怀疑 我现在还是不会写T2

T1 又又又又都错题了 下次重建图 尽量写vector 都写 邻接表 变量差不多的容易搞混 我这个同学变又写错了 

T1 :https://nanti.jisuanke.com/t/41086

题目大意就是 一个有向图 删一个点 把与他直接和间接 相连的点 删掉 然后 求删掉所有点的最小最大代价 ;

为了避免这个环的情况 我们显然是先 tarjan 求一下强连通分量 然后考虑 缩点 显然是删掉这个强连通分量的任意一个点 这个强连通分量都会被删掉 

但是题目要求我们求出最大最小代价 那我们在求tarjan的过程中 维护每一个块的最大最小代价

那么对于最小代价 显然是缩点之后入度为0的节点的代价的累和 

对于最大代价 我们反向建图 从叶子结点往上便利 然后每一个连通块的最大代价 累和 即可

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
template<typename T>inline void read(T &x) {
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    if(ch=='-') f=-1, ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}
const int N=250000;
int n,m,x,y,cnt=0,ans1,ans2,num,tot,tc,top;
int in1[N],in2[N],Stack[N],minn[N],cost[N],maxx[N],dfn[N],low[N],lin[N],linc[N],ins[N],belong[N];
struct gg {
    int y,next;
}a[N],e[N];
inline void add(int x,int y) {
    a[++tot].y=y;
    a[tot].next=lin[x];
    lin[x]=tot;
}
inline void add_c2(int x,int y) {
    e[++tc].y=y;
    e[tc].next=linc[x];
    linc[x]=tc;
}
inline void tarjan(int x) {
    dfn[x]=low[x]=++num;
    Stack[++top]=x; ins[x]=1;
    for(int i=lin[x];i;i=a[i].next) {
        int y=a[i].y;
        if(!dfn[y]) {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(ins[y]) 
            low[x]=min(low[x],dfn[y]);
    }
    if(dfn[x]==low[x]) {
        ++cnt;
        int k;
        do {
            k=Stack[top--];
            ins[k]=0;
            belong[k]=cnt;
            minn[cnt]=min(minn[cnt],cost[k]);
            maxx[cnt]=max(maxx[cnt],cost[k]);
        }while(x!=k);
    }
}
inline void topsort() {
    queue<int>q;
    for(int i=1;i<=cnt;i++) if(!in2[i]) q.push(i);
    while(q.size()) {
        int x=q.front();ans2+=maxx[x]; q.pop();
        for(int i=linc[x];i;i=e[i].next) {
            int y=e[i].y;
            in2[y]--;
            if(!in2[y]) q.push(y);
        }
    }
}
int main() {
    //freopen("1.in.cpp","r",stdin);
    read(n); read(m);
    for(int i=1;i<=n;i++) read(cost[i]),minn[i]=inf,maxx[i]=-inf;
    for(int i=1;i<=m;i++) {
        read(x); read(y);
        add(x,y);
    } 
    for(int i=1;i<=n;i++) {
        if(!dfn[i]) tarjan(i);
    }
    for(int i=1;i<=n;i++) {
        //cout<<maxx[i]<<endl;
        for(int j=lin[i];j;j=a[j].next) {
            int y=a[j].y;
            if(belong[i]!=belong[y]) {
                //add_c(belong[i],belong[y]);没必要建图了; 
                add_c2(belong[y],belong[i]);
                in1[belong[y]]++;
                in2[belong[i]]++;
            }
        }
    }
    ans1=ans2=0;
    for(int i=1;i<=cnt;i++) 
        if(!in1[i]) ans1+=minn[i];
    topsort();
    printf("%d %d\n",ans1,ans2);
    return 0;
} 

T2 恶心数学题??? 原来只做过一个简化版的qwq 模拟赛当时考了 然后暴毙了;

那个模拟赛 我也没有写题解 那我在这里总结一下好吧 题目大意:

从(0,0)这个点 不跨过 y=x 和 y=x-(n-m) 到达(n,m)的方案数 保证 n>=2m

显然需要用不考虑跨过任何一条直线的方案数-跨过某一条直线的方案数*2+跨过两条的方案数;

总的方案数 $\tbinom{n+m}{m}$

然后考虑 跨过某一条直线的方案数 $\tbinom{n+m}{m-1}$

因为题目保证n>=2m 所以跨过第二条直线的时候 就没有机会跨过第一个了qwq 所以一定是 先跨过第一条线 在跨过第二条线

用和卡特兰数类似的方法也能求出来 方案数 $\tbinom{n+m}{m}$-$\tbinom{n+m}{m-1}$*2+$\tbinom{n+m}{m-2}$


这个T2 就比较恶心了 目标就是求从 (0,0)走到 (n,m)  且不经过 y=x+a 和 y=x+b 这两条的方案数,模数为 998244353 。

考虑 怎么做呢 其实 我的思路也是在不断 订正的 我第一遍看到 不会写 只会分类讨论 但是 刘神 指导说 应该是有通解的 

那么我们不妨一点一点思考 讨论一下 这种组合数求解 这种问题的方法 需要几个简单例子的引入


如果 给定 (n,m) 和一个点 p(a,b) 只能往右 往上走 求出 不经过这个点p 到达终点 的方案数

那么我们也是用所有的方案数 减去 经过 p的 方案数 

总的方案数 $\tbinom{n+m}{m}$ 然后考虑 一定经过p的方案数 那么我们先从(0,0) 走到 (a,b) 的方案数  再求出 (a,b) 走到(n,m)的方案数 这样我们显然 

可以推出 方案数为$\tbinom{n+m}{m}$-$\tbinom{a+b}{b}$*$\tbinom{m+n-a-b}{n-a}$

猜你喜欢

转载自www.cnblogs.com/Tyouchie/p/11577290.html