「CodePlus 2017 12 月赛」白金元首与独舞
Posted cjfdf
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「CodePlus 2017 12 月赛」白金元首与独舞相关的知识,希望对你有一定的参考价值。
description
data range
[ 1 leq T leq 10, 1 leq n, m leq 200 , 0 leq k leq min(nm, 300)]
solution
矩阵树定理
求无向图的生成树个数
度数矩阵-邻接矩阵
去掉一行一列求行列式
为了保证精度可以辗转相除
这里是模意义下的
const int mod=998244353;
int a[305][305];
il int gauss(int n){
RG int ans=1;
for(RG int i=2;i<=n;i++){
for(RG int j=i+1;j<=n;j++)
while(a[j][i]){
RG int t=a[i][i]/a[j][i];
for(RG int k=i;k<=n;k++)dec(a[i][k],1ll*t*a[j][k]%mod);
swap(a[i],a[j]);if(ans)ans=mod-ans;
}
ans=1ll*ans*a[i][i]%mod;
}
if(ans<0)ans+=mod;return ans;
}
有向图的外向(父亲指向儿子)生成树个数
对于每条边((u,v)),(a[v][v]++,a[u][v]--)(把度数看成入度)
然后直接求行列式即可
这题的解法
首先判掉无解
然后我们发现题目中每个空格方向的选择决定了空格之间的到达关系
于是这道题目变成了一个求内向生成树个数的题
套上矩阵树定理即可
code
#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cstring>
#include<complex>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<ctime>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define FILE "a"
#define mp make_pair
#define pb push_back
#define RG register
#define il inline
using namespace std;
typedef unsigned long long ull;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
const dd eps=1e-10;
const int mod=1e9+7;
const int N=2000010;
const dd pi=acos(-1);
const int inf=2147483645;
const ll INF=1e18+1;
const ll P=100000;
il ll read(){
RG ll data=0,w=1;RG char ch=getchar();
while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘))ch=getchar();
if(ch==‘-‘)w=-1,ch=getchar();
while(ch<=‘9‘&&ch>=‘0‘)data=data*10+ch-48,ch=getchar();
return data*w;
}
il void file(){
srand(time(NULL)+rand());
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
}
int n,m,p[305][305],kx[305],ky[305];
int id[305][305],tag[305][305],t[100010],cnt,tot;
int a[305][305];
int dx[]={0,0,-1,1},dy[]={-1,1,0,0};
il void init(){
memset(id,0,sizeof(id));
memset(tag,0,sizeof(tag));
memset(t,0,sizeof(t));
memset(a,0,sizeof(a));
n=read();m=read();tot=1;cnt=0;
for(RG int i=1,c;i<=n;i++)
for(RG int j=1;j<=m;j++){
a[i][j]=tag[i][j]=id[i][j]=c=0;
while(c!=‘L‘&&c!=‘R‘&&c!=‘U‘&&c!=‘D‘&&c!=‘.‘)c=getchar();
if(c==‘L‘)p[i][j]=0;if(c==‘R‘)p[i][j]=1;
if(c==‘U‘)p[i][j]=2;if(c==‘D‘)p[i][j]=3;
if(c==‘.‘){p[i][j]=4;id[i][j]=++tot;kx[tot]=i;ky[tot]=j;}
}
}
il bool work(){
for(RG int i=1;i<=n;i++)
for(RG int j=1;j<=m;j++)
if(!id[i][j]&&!tag[i][j]){
RG int x=i,y=j,w=p[i][j];cnt++;
RG int xx=x+dx[w],yy=y+dy[w];
while(!id[x][y]&&!tag[x][y]&&x>0&&y>0&&x<=n&&y<=m){
tag[x][y]=cnt;x=xx;y=yy;w=p[x][y];xx=x+dx[w];yy=y+dy[w];
}
if(id[x][y])t[cnt]=id[x][y];
else if(x<1||y<1||x>n||y>m)t[cnt]=1;
else if(tag[x][y]==cnt)return 0;
else if(tag[x][y])t[cnt]=t[tag[x][y]];
}
return 1;
}
il void upd(int &a,int b){a+=b;if(a>=mod)a-=mod;}
il void dec(int &a,int b){if(b)upd(a,mod-b);}
il int gauss(int n){
RG int ans=1;
for(RG int i=2;i<=n;i++){
for(RG int j=i+1;j<=n;j++)
while(a[j][i]){
RG int t=a[i][i]/a[j][i];
for(RG int k=i;k<=n;k++)dec(a[i][k],1ll*t*a[j][k]%mod);
swap(a[i],a[j]);if(ans)ans=mod-ans;
}
ans=1ll*ans*a[i][i]%mod;
}
if(ans<0)ans+=mod;return ans;
}
il int solve(){
for(RG int i=2;i<=tot;i++)
for(RG int w=0;w<=3;w++){
RG int xx=kx[i]+dx[w],yy=ky[i]+dy[w];
RG int u=t[tag[xx][yy]],v=i;
if(id[xx][yy])u=id[xx][yy];
if(xx<1||yy<1||xx>n||yy>m)u=1;
if(u==v||!u)continue;
a[v][v]++;if(!a[u][v])a[u][v]=mod;a[u][v]--;
}
return gauss(tot);
}
int main()
{
RG int T=read();
while(T--){
init();
if(!work()){puts("0");continue;}
else printf("%d
",solve());
}
return 0;
}
以上是关于「CodePlus 2017 12 月赛」白金元首与独舞的主要内容,如果未能解决你的问题,请参考以下文章
走进矩阵树定理--「CodePlus 2017 12 月赛」白金元首与独舞
LibreOJ#6299. 「CodePlus 2018 3 月赛」白金元首与克劳德斯
bzoj5133[CodePlus2017年12月]白金元首与独舞 并查集+矩阵树定理