「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\n",solve());
    }
    return 0;
}

原文地址:https://www.cnblogs.com/cjfdf/p/9497567.html

时间: 2024-10-07 13:09:15

「CodePlus 2017 12 月赛」白金元首与独舞的相关文章

走进矩阵树定理--「CodePlus 2017 12 月赛」白金元首与独舞

n,m<=200,n*m的方阵,有ULRD表示在这个格子时下一步要走到哪里,有一些待决策的格子用.表示,可以填ULRD任意一个,问有多少种填法使得从每个格子出发都能走出这个方阵,答案取模.保证未确定的格子<=300. ...一脸懵逼地写了原本30实际20的暴力然后跪着啃了下论文 然而什么都没啃懂不如结论记下来: 首先矩阵行列式的定义:一个n*n的矩阵,行列式值为$\sum_{b是n的一个排列} \ \ \ \ \ ( (-1)^{b的逆序对数} \ \ \ \ \ * \prod_{i=1}^

「CodePlus 2017 12 月赛」火锅盛宴

n<=100000种食物,给每个食物煮熟时间,有q<=500000个操作:在某时刻插入某个食物:查询熟食中编号最小的并删除之:查询是否有编号为id的食物,如果有查询是否有编号为id的熟食,如果有熟食删除之,否则输出其离煮熟的最小时间:查询编号在[L,R]的熟食有多少.保证操作时间递增. 可以发现:针对某种食物的信息维护只需要知道其未煮熟的食物中煮熟时间的最小值,而所有的熟食都可以一起维护.为此可以:对每个食物开个队列存未熟食物,对所有食物开个优先队列以便及时把熟食从队列中提出来,并开个以编号为

「CodePlus 2017 12 月赛」火锅盛宴(模拟+树状数组)

1A,拿来练手的好题 用一个优先队列按煮熟时间从小到大排序,被煮熟了就弹出来. 用n个vector维护每种食物的煮熟时间,显然是有序的. 用树状数组维护每种煮熟食物的数量. 每次操作前把优先队列里煮熟时间<=当前时间的弹出,BIT上+1. 每次0操作把食物塞进优先队列和vector 每次1操作先看看树状数组里有没有数,没有输出angry,有的话在树状数组上二分找到最小的数. 每次2操作先看看树状数组里有没有这一种数,有的话输出并-1,没有的话看看vector有没有,有的话输出时间差,没有的话输出

【LIbreOJ】#6256. 「CodePlus 2017 12 月赛」可做题1

[题意]定义一个n阶正方形矩阵为"巧妙的"当且仅当:任意选择其中n个不同行列的数字之和相同. 给定n*m的矩阵,T次询问以(x,y)为左上角的k阶矩阵是否巧妙.n,m<=500,T<=10^5. [算法]数学 [题解] 可以证明每个矩阵是巧妙的当且仅当其每个2阶子矩阵均是巧妙的: 必要性:若该矩阵有一个不巧妙的2阶子矩阵,则其他部分选择相同的情况下(不涉及此两行列),这两行列的和不同,所以该矩阵不是巧妙的. 观察一个巧妙的2阶子矩阵 a1 a2 b1 b2 由a1+b2=a

[LOJ 6249]「CodePlus 2017 11 月赛」汀博尔

Description 有 n 棵树,初始时每棵树的高度为 H_i,第 i 棵树每月都会长高 A_i.现在有个木料长度总量为 S 的订单,客户要求每块木料的长度不能小于 L,而且木料必须是整棵树(即不能为树的一部分).现在问你最少需要等多少个月才能满足订单. Input 第一行 3 个用空格隔开的非负整数 n,S,L,表示树的数量.订单总量和单块木料长度限制.第二行 n 个用空格隔开的非负整数,依次为 H1,H2,…,Hn.第三行 n 个用空格隔开的非负整数,依次为 A1,A2,…,An. Ou

「CodePlus 2017 11 月赛」汀博尔 (二分答案)

题目链接:https://loj.ac/problem/6249 题意:有 n 棵树,初始时每棵树的高度为 H?i?,第 i 棵树每月都会长高 A?i??.现在有个木料长度总量为 S 的订单,客户要求每块木料的长度不能小于 L,而且木料必须是整棵树(即不能为树的一部分). 现在问你最少需要等多少个月才能满足订单.(数据范围:1<=n<=200000,1<=S,L<=1e18,1<=Hi,Ai<=1e9) 题解:很显然二分答案.上界不能直接选择1e18,会爆long lo

「CodePlus 2017 11 月赛」可做题

这种题显然先二进制拆位,显然改的位置显然只有每一段确定的数的开头和结尾,只需要对于每一个可决策位置都尝试一下填1和0,然后取min即可. #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<algorithm> #define ll long long using namespace std; const int maxn=500010;

「CodePlus 2017 11 月赛」Yazid 的新生舞会(树状数组/线段树)

学习了新姿势..(一直看不懂大爷的代码卡了好久T T 首先数字范围那么小可以考虑枚举众数来计算答案,设当前枚举到$x$,$s_i$为前$i$个数中$x$的出现次数,则满足$2*s_r-r > 2*s_l-l$的区间$[l+1,r]$其众数为$x$,这个显然可以用一个数据结构来维护. 直接扫一遍效率是$O($数字种类数$*nlogn)$的,无法承受,但是我们发现,对于每一段非$x$的数,$2*s_i-i$是公差为$-1$的等差数列,所以它们对答案的贡献实际上可以一次性计算.设$L$为一段非$x$数

「THUSCH 2017」大魔法师 解题报告

「THUSCH 2017」大魔法师 狗体面太长,帖链接了 思路,维护一个\(1\times 4\)的答案向量表示\(A,B,C,len\),最后一个表示线段树上区间长度,然后每次的操作都有一个转移矩阵,随便搞搞就成了,卡常 Code: #include <cstdio> #include <cstring> namespace io { const int SIZE=(1<<21)+1; char ibuf[SIZE],*iS,*iT,obuf[SIZE],*oS=ob