Arc083_F Collecting Balls

传送门

题目大意

给定$N$,在$(1,0),(2,0)......(N,0)$和$(0,1),(0,2)...(0,N)$上都有$1$个机器人,同时给定$2N$个坐标$(x,y),x,y\in[1,N]$上有障碍,你每次可以选择一个没有被选过的机器人$K$,若$X_K=0$,则它会沿着$y$轴正方向移动直到遇到遇到障碍或移出边界$[0,N]$,若它遇到障碍,则它会和障碍一同消失。

求选择$2N$个机器人的$(2N!)$中顺序中,有多少种会使得所有障碍消失。

 

题解

神仙题

考虑把每一行和每一列看做$2N$个点,对于障碍$(x,y)$,视为在$x$行$y$列之间连一条边。

显然,一条边只能被它的两个端点删掉,同一,一个点端点只能用于删掉一个条边。

那么对于每一个连通块,当且仅当它是一个基环树时才可能有解。

考虑树边,显然叶子节点只能删掉连接它的边,那么同理可得树边只能被距离环更远的端点删掉。

而环上的边也只有两种情况,每个点要么顺时针删去下一条边,要么逆时针删去下一条边,枚举即可。

假设我们已经确定了每个点删掉了哪条边。

假设我们用$x$点删掉了$(x,y)$,那么考虑$y$是$(x,y)$的另一维坐标,那么对于$x$连接的$k$,若$k<y$,删除$(x,y)$之前一定要先删除$(x,k)$,所以$k$一定要在$x$之前先选,可以再建一个图,新图中$k$向$x$连边。

于是我们就得到了一个有先后顺序要求的拓扑图。显然这是一个森林,因为对于任意点对$(a,b)$,一定不会存在两种方式从$a$到达$b$,而且新图上的边在原图中一定是从树枝的叶子一步步连向环的某一个节点,所以森林是若干颗内向树构成的。

考虑有$m$个点的内向树有多少种拓扑序。共有$m!$中排序方式,其中对于以$x$为根的子树,它可能是拓扑序当且仅当它是这$Size_x$个节点中($Size_x$表示其子树大小)的最后一个(在外向树中应该是第一个,所以有向树在去掉方向后同构则拓扑序数量),那么它是有效的序列只有$\frac{1}{Size_x}$种,每一棵子树可以独立考虑,所以拓扑序方案数就是$\frac{m!}{\prod\limits_{x=1}^{m} Size_x}$

对于一棵内向树森林,则$Size$定义不变,$m$变为森林中所有点的点数。

由于环上的选边方式有两种,就枚举方向再加起来,就得到了消完这个连通块的方案数。

对于连通块$i$,设其点数为$m_i$,方案数为$Ans_i$,一共有$K$个连通块。

最后的总方案数是$$\prod\limits_{i=1}^{K}\dbinom{\sum\limits_{j=i}^{K}m_j}{m_i}\times Ans_i$$

虽然已经可以算了,但是我们还可以通过把$\dbinom{n}{m}$化为$\frac{n!}{m!(n-m)!}$的形式,不难发现这个上式就变成了$$\frac{(2N)!}{\prod\limits_{i=1}^{K}m_i}\prod\limits_{i=1}^{K}Ans_i$$更加直观且容易计算。

复杂度$O(n)$。

 

#include<bits/stdc++.h>
#define LL long long
#define M 200020
#define mod 1000000007
using namespace std;
namespace IO{
    const int BS=(1<<22)+5; int Top=0;
    char Buffer[BS],OT[BS],*OS=OT,*HD,*TL,SS[20]; const char *fin=OT+BS-1;
    inline char Getchar(){if(HD==TL){TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);} return (HD==TL)?EOF:*HD++;}
    inline void write(int x){
        if(!x){putchar('0');return;} if(x<0) x=-x,putchar('-');
        while(x) SS[++Top]=x%10,x/=10;
        while(Top) putchar(SS[Top]+'0'),--Top;
    }
    inline int read(){
        int nm=0; char cw=Getchar(); for(;!isdigit(cw);cw=Getchar());
        for(;isdigit(cw);cw=Getchar()) nm=nm*10+(cw-'0'); return nm;
    }
}
using namespace IO;
#define mul(x,y) (LL)(x)*(y)%mod 
#define upd(x,y) x=((x)+(y)>=mod)?(x)+(y)-mod:(x)+(y)
int n,m,fs[M],nt[M<<1],to[M<<1],tmp,ans=1,EG,top,ind[M],node[M];
int S[M],c[M],fa[M],nxt[M],fst[M],tar[M],cur,cnt,sz[M],inv[M],vis[M];
inline void link(int x,int y){
    nt[tmp]=fs[x],fs[x]=tmp,to[tmp++]=y;
    nt[tmp]=fs[y],fs[y]=tmp,to[tmp++]=x;
}
inline void edge(int x,int y){nxt[cur]=fst[x],fst[x]=cur,tar[cur++]=y;}
void init(int x,int last){
    node[++cnt]=x,S[++top]=x,ind[x]=1,S[top+1]=0;
    for(register int i=fs[x];~i;i=nt[i],EG++){
        if(to[i]==last) continue;
        if(!ind[to[i]]){init(to[i],x);continue;}
		if(m) continue;
        while(S[top+1]!=to[i]) vis[c[++m]=S[top--]]=true; c[0]=to[i];
    } top--,ind[x]=2;
}
void build(int x,int last){
    for(register int i=fs[x];~i;i=nt[i]){
        if(vis[to[i]]||to[i]==last) continue;
        fa[to[i]]=x,build(to[i],x);
    }
}
int dfs(int x){
    if(sz[x]) return 1; int res=sz[x]=1;
    for(register int i=fst[x];i!=-1;i=nxt[i]){
        res=mul(res,dfs(tar[i])),sz[x]+=sz[tar[i]];
    } return mul(res,inv[sz[x]]);
}
inline int calc(){
    for(int i=1;i<=cnt;i++) fst[node[i]]=-1,sz[node[i]]=0; cur=0; int res=1;
    for(int x=1;x<=cnt;x++) for(register int i=fs[node[x]];i!=-1;i=nt[i]) if(to[i]<fa[node[x]]) edge(node[x],to[i]);
    for(int x=1;x<=cnt;x++) if(!sz[node[x]]) res=mul(res,dfs(node[x])); return res;
}
inline int solve(int x){
    top=0,init(x,0); if(EG!=(cnt<<1)) puts("0"),exit(0);
    for(int i=1;i<=m;i++) build(c[i],0); int res=0;
    for(int i=1;i<=m;i++) fa[c[i-1]]=c[i]; upd(res,calc());
    for(int i=1;i<=m;i++) fa[c[i]]=c[i-1]; upd(res,calc());
    return res;
}
int main(){
    n=read(),ans=inv[1]=1,memset(fs,-1,sizeof(fs));
    for(int i=2;i<=(n<<1);i++) ans=mul(ans,i),inv[i]=mul(inv[mod%i],mod-(mod/i));
    for(int i=1;i<=(n<<1);i++) link(read()+n,read());
    for(int i=1;i<=(n<<1);i++) if(!ind[i]) cnt=m=EG=0,ans=mul(ans,solve(i));
    printf("%d\n",ans); return 0;
}

 

转载于:https://www.cnblogs.com/OYJason/p/9846010.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
图像识别技术在病虫害检测中的应用是一个快速发展的领域,它结合了计算机视觉和机器学习算法来自动识别和分类植物上的病虫害。以下是这一技术的一些关键步骤和组成部分: 1. **数据收集**:首先需要收集大量的植物图像数据,这些数据包括健康植物的图像以及受不同病虫害影响的植物图像。 2. **图像预处理**:对收集到的图像进行处理,以提高后续分析的准确性。这可能包括调整亮度、对比度、去噪、裁剪、缩放等。 3. **特征提取**:从图像中提取有助于识别病虫害的特征。这些特征可能包括颜色、纹理、形状、边缘等。 4. **模型训练**:使用机器学习算法(如支持向量机、随机森林、卷积神经网络等)来训练模型。训练过程中,算法会学习如何根据提取的特征来识别不同的病虫害。 5. **模型验证和测试**:在独立的测试集上验证模型的性能,以确保其准确性和泛化能力。 6. **部署和应用**:将训练好的模型部署到实际的病虫害检测系统中,可以是移动应用、网页服务或集成到智能农业设备中。 7. **实时监测**:在实际应用中,系统可以实时接收植物图像,并快速给出病虫害的检测结果。 8. **持续学习**:随着时间的推移,系统可以不断学习新的病虫害样本,以提高其识别能力。 9. **用户界面**:为了方便用户使用,通常会有一个用户友好的界面,显示检测结果,并提供进一步的指导或建议。 这项技术的优势在于它可以快速、准确地识别出病虫害,甚至在早期阶段就能发现问题,从而及时采取措施。此外,它还可以减少对化学农药的依赖,支持可持续农业发展。随着技术的不断进步,图像识别在病虫害检测中的应用将越来越广泛。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值