BJOI 2019 模拟赛 #2 题解
T1 完美塔防
有一些空地,一些障碍,一些炮台,一些反射镜
障碍会挡住炮台的炮,
反射镜可以 90° 反射炮台的光线,炮台可以选择打他所在的水平一条线或者竖直一条线
求是否有一组方案满足每个空地必须要被至少一个炮台打到,且每个炮台都不能被炮台打到
用 $n \times m$ 的字符矩形给出,有方案的话需要给出构造
$n,m \leq 50$
有 $200$ 组测试数据
sol:
考场上看到这题肯定是先想网络流,上下界建出来发现...我建的某一种边要求“流 $0$ 或者流 $2$”
然后我就不会了,考完发现这是 2-sat 裸题...
如果知道 2-sat 就不用那么麻烦了,可以一开始可以预处理出每个炮台必须打的方向和每个炮台不能打的方向
然后发现如此操作后每个空地最多被两个炮台攻击,则这个空地相当于一个条件“选 $A$ 或者选 $B$”
然后就是 2-sat 了
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <memory.h>
#include <vector> using namespace std;
typedef long long LL;
#define pb push_back const int maxn = ;
int dx[]={-,,,};
int dy[]={,,,-}; vector<int> adj[maxn],ebk[maxn],cov[maxn][maxn];
char str[maxn][maxn];int R,C,T;
bool _cov[maxn][maxn];
bool cov0[maxn][maxn];
bool cov1[maxn][maxn];
int id[maxn][maxn],tot,scc[maxn],iscc; int dfn[maxn],low[maxn],idx;
int stk[maxn],top;bool ins[maxn];
int que[maxn],head,tail,deg[maxn],pos[maxn]; #define clr(_) memset(_,0,sizeof _)
bool inG(int x,int y) {
return x>=&&x<=R&&y>=&&y<=C&&str[x][y]!='#';
}
bool beam(int x,int y) {
return str[x][y]=='|'||str[x][y]=='-';
}
bool dfs(bool cov[maxn][maxn],int x,int y,int d) {
cov[x][y]=true;
if (!inG(x+dx[d],y+dy[d]))
return true;
int nx=x+dx[d],ny=y+dy[d];
if (beam(nx,ny)) return false;
if (str[nx][ny]=='.') return dfs(cov,nx,ny,d);
else return dfs(cov,nx,ny,d^(str[nx][ny]=='/'?:));
}
void tarjan(int u) {
dfn[u]=low[u]=++idx;
stk[++top]=u;ins[u]=true;
for (unsigned j=;j<adj[u].size();j++)
{
int v=adj[u][j];
if (!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
else if (ins[v]) low[u]=min(low[u],dfn[v]);
}
if (low[u]!=dfn[u]) return ;
scc[u]=++iscc;
while (stk[top]!=u) {
int v=stk[top--];
ins[v]=false;scc[v]=iscc;
}
ins[u]=false;top--;
}
void solve()
{
scanf("%d %d",&R,&C);
for (int i=;i<=R;i++)
scanf("%s",str[i]+);
for (int i=;i<=tot*+;i++)
adj[i].clear(),ebk[i].clear();
for (int i=;i<=R;i++)
for (int j=;j<=C;j++) cov[i][j].clear();
clr(_cov);clr(dfn);tot=idx=iscc=; for (int i=;i<=R;i++)
for (int j=;j<=C;j++)
if (beam(i,j)) {
id[i][j]=++tot;
clr(cov0);bool tag0=dfs(cov0,i,j,)&dfs(cov0,i,j,);
clr(cov1);bool tag1=dfs(cov1,i,j,)&dfs(cov1,i,j,);
if (!tag0&&!tag1) {printf("IMPOSSIBLE\n");return ;}
if (!tag0) {
for (int x=;x<=R;x++)
for (int y=;y<=C;y++)
if (str[x][y]=='.'&&cov1[x][y])
cov[x][y].pb(tot<<|);
adj[tot<<].pb(tot<<|);
}
else if (!tag1) {
for (int x=;x<=R;x++)
for (int y=;y<=C;y++)
if (str[x][y]=='.'&&cov0[x][y])
cov[x][y].pb(tot<<);
adj[tot<<|].pb(tot<<);
}
else {
for (int x=;x<=R;x++)
for (int y=;y<=C;y++)
if (str[x][y]=='.') {
if (cov0[x][y]&&cov1[x][y]) _cov[x][y]=true;
else if (cov0[x][y]) cov[x][y].pb(tot<<);
else if (cov1[x][y]) cov[x][y].pb(tot<<|);
}
}
}
for (int i=;i<=R;i++)
for (int j=;j<=C;j++)
if (str[i][j]=='.'&&!_cov[i][j])
{
int siz=cov[i][j].size(),u,v;
if (siz==) {printf("IMPOSSIBLE\n");return ;}
else if (siz==) u=cov[i][j][],adj[u^].pb(u);
else if (siz==) u=cov[i][j][],v=cov[i][j][],adj[u^].pb(v),adj[v^].pb(u);
} for (int i=;i<=(tot<<|);i++)
if (!dfn[i]) tarjan(i);
for (int i=;i<=tot;i++)
if (scc[i<<]==scc[i<<|]) {printf("IMPOSSIBLE\n");return ;}
for (int v,u=;u<=(tot<<|);u++)
for (unsigned j=;j<adj[u].size();j++)
if (scc[v=adj[u][j]]!=scc[u])
ebk[scc[v]].pb(scc[u]),deg[scc[u]]++; head=;tail=;
for (int i=;i<=iscc;i++)
if (!deg[i]) que[++tail]=i;
while (head<=tail) {
int u=que[head];pos[u]=head++;
for (unsigned j=;j<ebk[u].size();j++)
{
int v=ebk[u][j];
if (!--deg[v]) que[++tail]=v;
}
} for (int i=;i<=tot*+;i++)
ebk[i].clear(),adj[i].clear();
printf("POSSIBLE\n");
}
int main(){
for (cin >> T; T; T--) solve();
}
T2 生成树计数
给一个 $n$ 个点的无向带边权完全图 $G$,求 $\sum\limits_{T 是 G 的生成树} \space \space \space \space \space (\sum\limits_{i \in E_T} w_i)^k$,膜 $998244353$
$n,k \leq 30$
sol:
考场上很 naive 的以为矩阵树定理求的是边权和,然而求的是边权乘积...
于是大概就是要把一个和的 $k$ 次方转化成积
然后就有一个公式 $(\sum\limits_{i=1}^m w_i)^k = k! \times [z^k] \prod\limits_{i=1}^m e^{w_i \times z} = k! \times [z^k] \prod\limits_{i=1}^m \sum\limits_{t} \frac{w_i^t \times z^t}{t!}$
然后从它里面提取出一个“边权乘积”,即 $\prod\limits_{i=1}^m \sum\limits_{t} \frac{w_i^t \times z^t}{t!}$
所以“边权”就是 $\sum\limits_{t} \frac{w_i^t \times z^t}{t!}$ 由于只需要第 $k$ 项,这个和式的 $t$ 取到 $[0,k]$ 就可以了
然后就每条边维护一下这个东西,再矩阵树定理即可
因为数据比较小,多项式部分暴力就可以了
复杂度 $O(n^3 \times k^2)$
#include <bits/stdc++.h>
#define LL long long
#define rep(i, s, t) for(register int i = (s), i##end = (t); i <= i##end; ++i)
#define dwn(i, s, t) for(register int i = (s), i##end = (t); i >= i##end; --i)
using namespace std;
inline int read() {
int x = , f = ; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -f;
for(; isdigit(ch); ch = getchar()) x = * x + ch - '';
return x * f;
}
const int maxn = , mod = ;
int n, k, ifac[maxn];
int a[maxn][maxn][maxn], mt[maxn][maxn], res[maxn], _inv[maxn], _tmp[maxn], _dec[maxn];
inline int mo(int x) { return ((x % mod) + mod) % mod; }
inline int inc(int x, int y) { x += y; if(x >= mod) x -= mod; return x; }
inline int dec(int x, int y) { x -= y; if(x < ) x += mod; return x; }
inline int mul(int x, int y) { return 1LL * x * y % mod; }
inline int ksm(int x, int t, int res = ) {
for(; t; x = mul(x, x), t = t >> )
if(t & ) res = mul(res, x); return res;
}
#define inv(x) (ksm(x, mod-2))
inline void mul(int *a, int *b, int *res) {
static int tmp[maxn];
memset(tmp, , sizeof(tmp));
rep(i, , k) rep(j, , k-i) tmp[i + j] = inc(tmp[i + j], mul(a[i], b[j]));
memcpy(res, tmp, sizeof(tmp));
}
inline void inverse(int *a, int *res) {
static int tmp[maxn], temp[maxn];
memset(tmp, , sizeof(tmp));
tmp[] = inv(a[]);
rep(i, , k) temp[i] = mul(a[i], tmp[]);
rep(i, , k) rep(j, , i) tmp[i] = inc(tmp[i], mul(dec(, temp[j]), tmp[i - j]));
memcpy(res, tmp, sizeof(tmp));
}
int main() {
n = read(), k = read();
ifac[] = ifac[] = ;
rep(i, , k) ifac[i] = mul(dec(mod, mod / i), ifac[mod % i]);
rep(i, , k) ifac[i] = mul(ifac[i], ifac[i - ]);
rep(i, , n) rep(j, , n) mt[i][j] = read();
rep(i, , n) rep(j, , i-) {
int pw = ;
rep(l, , k) {
a[i][j][l] = a[j][i][l] = dec(, mul(pw, ifac[l]));
a[i][i][l] = inc(a[i][i][l], mul(pw, ifac[l]));
a[j][j][l] = inc(a[j][j][l], mul(pw, ifac[l]));
pw = mul(pw, mt[i][j]);
}
} n--; res[] = ;
rep(i, , n) {
inverse(a[i][i], _inv);
rep(kk, i+, n) {
mul(_inv, a[kk][i], _tmp);
rep(l, i, n) {
mul(_tmp, a[i][l], _dec);
rep(t, , k) a[kk][l][t] = dec(a[kk][l][t], _dec[t]);
}
}
}
rep(i, , n) mul(res, a[i][i], res);
int ans = res[k]; rep(i, , k) ans = mul(ans, i);
cout << ans << endl;
}
T3 图的难题
给一个图,如果存在一种染色方案把图染成黑白两色,黑色子图没有环,白色子图没有环,则称这个图合法,给一个图判断合不合法
$n \leq 500,m \leq 1000$
sol:
大力猜结论
如果一个非空子图不合法,则整个图都不合法
一个子图合法当且仅当 $e \leq 2 \times (v-1)$
意会一下,发现大概很对(大概能拆成两个树,大概多一条边就成环了)
然后令点的权为 $-2$,边的权为 $1$,每次堵住一条边然后最大权闭合图即可
堵住一条边就是先算一下这条边已经流了多少,把这些流全退了,再跑一遍最大流
这样做是因为裸跑最大权闭合图的话算出来肯定是 $0$ (空图)而我们要强制非空
#include <bits/stdc++.h>
#define LL long long
#define rep(i, s, t) for(register int i = (s), i##end = (t); i <= i##end; ++i)
#define dwn(i, s, t) for(register int i = (s), i##end = (t); i >= i##end; --i)
using namespace std;
inline int read() {
int x = , f = ; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -f;
for(; isdigit(ch); ch = getchar()) x = * x + ch - '';
return x * f;
}
const int maxn = ;
int n, m, S, T;
struct Dinic {
int m, s, t;
int vis[maxn], _tim, dis[maxn]; queue<int> q;
int cur[maxn], head[maxn], nx[maxn * ];
int flow;
void init(int n) {
memset(head, -, sizeof(head));
memset(cur, -, sizeof(cur));
memset(vis, , sizeof(vis));
flow = ; m = ; _tim = ;
}
struct Edge {
int from, to, caps;
Edge() {}
Edge(int _u, int _v, int _w) : from(_u), to(_v), caps(_w){}
}es[maxn * ];
inline void add(int u, int v, int w) {
es[m] = Edge(u, v, w); nx[m] = head[u]; head[u] = m++;
es[m] = Edge(v, u, ); nx[m] = head[v]; head[v] = m++;
}
int BFS() {
vis[t] = ++_tim; dis[t] = ; q.push(t);
while(!q.empty()) {
int now = q.front(); q.pop(); cur[now] = head[now];
for(int i=head[now];~i;i=nx[i]) {
Edge &e = es[i ^ ];
if(e.caps && (vis[e.from] != _tim)) {
q.push(e.from);
vis[e.from] = _tim;
dis[e.from] = dis[now] + ;
}
}
}
return (vis[s] == _tim);
}
int DFS(int u, int a) {
//cout << u << endl;
if(u == t || !a) return a;
int flow = , f;
for(int &i=cur[u];~i;i=nx[i]) {
Edge &e = es[i];
if(dis[e.to] == dis[u] - && (f = DFS(e.to, min(e.caps, a)))) {
flow += f; e.caps -= f; es[i ^ ].caps += f; a -= f;
if(!a) return flow;
}
} return flow;
}
int MaxFlow(int _s, int _t, int mx = ) {
s = _s, t = _t; int flw = ;
while(BFS() && mx) flw += DFS(s,mx), mx -= flw;
if(s == S) return (flow += flw);
return flw;
}
void block(int pos) {
int mx = es[ * (pos - ) + ].caps;
int curflow = MaxFlow(T, pos, mx);
flow -= curflow;
}
} sol;
int u[maxn], v[maxn];
int solve() {
n = read(), m = read();
sol.init(n + m + ); S = n + m + , T = n + m + ;
rep(i, , m) u[i] = read(), v[i] = read();
rep(i, , n) sol.add(S, i, );
rep(i, , m) {
sol.add(i + n, T, );
sol.add(v[i], i + n, );
sol.add(u[i], i + n, );
}
rep(i, , n) {
sol.block(i); sol.es[ * (i - )].caps = sol.es[ * (i - ) + ].caps = ;
if(m > sol.MaxFlow(S, T)) return ;
sol.es[ * (i - )].caps = ;
} return ;
}
int main() {
dwn(T, read(), ) puts(solve() ? "Yes" : "No");
return ;
}
BJOI 2019 模拟赛 #2 题解的更多相关文章
- 【洛谷比赛】[LnOI2019]长脖子鹿省选模拟赛 T1 题解
今天是[LnOI2019]长脖子鹿省选模拟赛的时间,小编表示考的不怎么样,改了半天也只会改第一题,那也先呈上题解吧. T1:P5248 [LnOI2019SP]快速多项式变换(FPT) 一看这题就很手 ...
- 2020.3.23 模拟赛游记 & 题解
这次的模拟赛,实在是水. 数据水,\(\texttt{std}\) 水,出题人水,做题人也水.??? 游记就说一句: 水. T1 metro 弱智题. 人均 \(100pts\). #pragma G ...
- BJOI 模拟赛 #3 题解
T1 一个网格,每个点有权值,求有多少条路径权值乘积不小于 $n$ $R,C \leq 300, n \leq 10^6$ sol: 暴力 dp 是 $O(R \times C \times n)$ ...
- bsoj5988 [Achen模拟赛]期望 题解
bsoj5988 Description [题目背景] NOI2018 已经过去了许久,2019 届的 BSOIer 们退役的退役,颓废的颓废,计数能力大不如前.曾经的数数之王 xxyj 坦言:&qu ...
- 洛谷[LnOI2019]长脖子鹿省选模拟赛 简要题解
传送门 听说比赛的时候T4T4T4标程锅了??? WTF换我时间我要写T3啊 于是在T4T4T4调半天无果的情况下260pts260pts260pts收场真的是tcltcltcl. T1 快速多项式变 ...
- NOIP模拟赛10 题解
t3: 题意 给你一棵树,然后每次两种操作:1.给一个节点染色 : 2. 查询一个节点与任意已染色节点 lca 的权值的最大值 分析 考虑一个节点被染色后的影响:令它的所有祖先节点(包括自身)的所有除 ...
- 【CYH-02】noip2018数论模拟赛:赛后题解
1.小奔的矩阵 2.大奔的方案 3.小奔与不等四边形 4.小奔的方案 当然本次比赛肯定难度不会仅限于此啦!后续还会--
- 20191216 GXOI 2019模拟赛 逼死强迫症
题目传送门 分析: sb矩阵加速推一辈子... 想了1个小时,结果好像还和标准答案的方法不一样诶... 标算解法: 老套路,对于新加入的一列,考虑它与目前最后一列的关系 我们可以列出四种方案: 其中前 ...
- contesthunter暑假NOIP模拟赛第一场题解
contesthunter暑假NOIP模拟赛#1题解: 第一题:杯具大派送 水题.枚举A,B的公约数即可. #include <algorithm> #include <cmath& ...
随机推荐
- js实现全选checkbox
js代码 function selectAllCheckBox(parentid) { var PID = document.getElementById(parentid); var cb = PI ...
- 浅谈C#泛型的定义、继承、方法和约束
摘要:本文介绍了如何定义一个C#泛型类,以及实现泛型类的继承.方法和约束. C#泛型参数化了类型,把类型作为参数抽象出来,从而使我们在实际的运用当中能够更好的实现代码的重复利用,同时它提供了更强的类型 ...
- Lubuntu系统中java,tomcat的环境搭建(virtualbox中)
一.安装Lubuntu系统 这一步没什么说的,到官网下载镜像,在virtualbox中安装即可安装时就已经可以选择安装源,当然,选中国的设置环装网络,可将该虚拟机设立为网络上的独立IP,和物理机间可以 ...
- thinkphp3.2.3定时任务 不能获取本模块config, 不能获取本模块的其他配置
一开始创建就有一个home模块再创建一个Data模块 定时任务在/Application/Common/Conf/crons.php中,这里不讲怎么创建定时任务. Data模块的配置文件路径如下/Ap ...
- A4纸网页打印 html网页页面的宽度设置成多少
A4纸竖向打印,html网页页面的宽度设置成多少?这个问题是我们大家所疑惑的,于是网上搜集整理下,希望可以帮助你们 最近开发项目时遇到了网页打印的问题,这是问题之二,打印宽度设置 在公制长度单位与屏幕 ...
- 利用python3.x实现小爬虫下载贴吧内图片
Hi, I'm back. 寒假在家只有一台笔记本,也懒得把台式机上的键盘拆下来用,因此编程被我暂时搁置,转而在网易云课堂上学了一下Python.可惜的是云课堂的Python教程是基于Python ...
- 图 Graph-图的相关算法
2018-03-06 17:42:02 一.最短路问题 问题描述:在网络中,求两个不同顶点之间的所有路径中,边的权值之和最小的那一条路径. 这条路径就是两点之间的最短路径 (Shortest Path ...
- 二十九 Python分布式爬虫打造搜索引擎Scrapy精讲—selenium模块是一个python操作浏览器软件的一个模块,可以实现js动态网页请求
selenium模块 selenium模块为第三方模块需要安装,selenium模块是一个操作各种浏览器对应软件的api接口模块 selenium模块是一个操作各种浏览器对应软件的api接口模块,所以 ...
- SPOJ-394-ACODE - Alphacode / dp
ACODE - Alphacode #dynamic-programming Alice and Bob need to send secret messages to each other and ...
- Java解析XML格式串(JDOM解析)
import java.io.IOException; import java.io.StringReader; import java.util.List; import org.jdom.Docu ...