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 题解的更多相关文章

  1. 【洛谷比赛】[LnOI2019]长脖子鹿省选模拟赛 T1 题解

    今天是[LnOI2019]长脖子鹿省选模拟赛的时间,小编表示考的不怎么样,改了半天也只会改第一题,那也先呈上题解吧. T1:P5248 [LnOI2019SP]快速多项式变换(FPT) 一看这题就很手 ...

  2. 2020.3.23 模拟赛游记 & 题解

    这次的模拟赛,实在是水. 数据水,\(\texttt{std}\) 水,出题人水,做题人也水.??? 游记就说一句: 水. T1 metro 弱智题. 人均 \(100pts\). #pragma G ...

  3. BJOI 模拟赛 #3 题解

    T1 一个网格,每个点有权值,求有多少条路径权值乘积不小于 $n$ $R,C \leq 300, n \leq 10^6$ sol: 暴力 dp 是 $O(R \times C \times n)$ ...

  4. bsoj5988 [Achen模拟赛]期望 题解

    bsoj5988 Description [题目背景] NOI2018 已经过去了许久,2019 届的 BSOIer 们退役的退役,颓废的颓废,计数能力大不如前.曾经的数数之王 xxyj 坦言:&qu ...

  5. 洛谷[LnOI2019]长脖子鹿省选模拟赛 简要题解

    传送门 听说比赛的时候T4T4T4标程锅了??? WTF换我时间我要写T3啊 于是在T4T4T4调半天无果的情况下260pts260pts260pts收场真的是tcltcltcl. T1 快速多项式变 ...

  6. NOIP模拟赛10 题解

    t3: 题意 给你一棵树,然后每次两种操作:1.给一个节点染色 : 2. 查询一个节点与任意已染色节点 lca 的权值的最大值 分析 考虑一个节点被染色后的影响:令它的所有祖先节点(包括自身)的所有除 ...

  7. 【CYH-02】noip2018数论模拟赛:赛后题解

    1.小奔的矩阵 2.大奔的方案 3.小奔与不等四边形 4.小奔的方案 当然本次比赛肯定难度不会仅限于此啦!后续还会--

  8. 20191216 GXOI 2019模拟赛 逼死强迫症

    题目传送门 分析: sb矩阵加速推一辈子... 想了1个小时,结果好像还和标准答案的方法不一样诶... 标算解法: 老套路,对于新加入的一列,考虑它与目前最后一列的关系 我们可以列出四种方案: 其中前 ...

  9. contesthunter暑假NOIP模拟赛第一场题解

    contesthunter暑假NOIP模拟赛#1题解: 第一题:杯具大派送 水题.枚举A,B的公约数即可. #include <algorithm> #include <cmath& ...

随机推荐

  1. nwafu - java实习 JDBC练习 - 学生信息系统界面

    学生信息系统界面的实现 - JDBC writer:pprp 登录界面的实现: 分为两个部分: 1.LoginFrame.java : 用windowbuilder进行快速搭建界面,构建好登录的界面, ...

  2. 2017 ACM-ICPC 南宁区比赛 Minimum Distance in a Star Graph

    2017-09-25 19:58:04 writer:pprp 题意看上去很难很难,但是耐心看看还是能看懂的,给你n位数字 你可以交换第一位和之后的某一位,问你采用最少的步数可以交换成目标 有五组数据 ...

  3. cygwin 获取root高级权限

    cygwin安装完成后没有passwd文件解决方法 ​

  4. 4.彻底理解synchronized

    1. synchronized简介 在学习知识前,我们先来看一个现象: public class SynchronizedDemo implements Runnable {    private s ...

  5. HTTP下载图片

    网上有专门写的http下载的C++代码,但是我发现windows自带的http下载,一行代码就搞定,非常简单,目前为止使用正常. 首先包含头文件和lib文件 #include <urlmon.h ...

  6. Hibernate[延迟加载] [三种状态] [脏检查] [缓存机制]

    一.持久化对象的唯一标识 java中按内存地址不同区分同一个类的不同对象,关系数据库用主键区分同一条记录,Hibernate使用OID来建立内存中的对象和数据库中记录的对应关系 什么是OID? 解析: ...

  7. Find the Longest Word in a String

    找到提供的句子中最长的单词,并计算它的长度. 函数的返回值应该是一个数字. 这是一些对你有帮助的资源: String.split() String.length 第一种想法就是,先定一个小变量,来他一 ...

  8. 华为EPON OLT开局配置

      配置思路: 1. 登录olt(console进去之后配地址) 2.配置上联口(配vlan和起三层地址互联路由的lan口) 3.epon接分光器,分光器下接光猫 4.自动发现光猫.配置DBA数据和线 ...

  9. 将封装了envi功能的IDL类导出成java类,方便java调用

    目的:     用IDL将ENVI的功能封装成为IDL的类,并使用IDL的对象导出功能把这些功能类导出为java类,方便java调用.(本来想直接通过GP工具调用的,但是没有授权文件)   操作步骤: ...

  10. Nginx+keepalived 脚本安装主从双机热备自动切换解决方案

    Nginx+keepalived 脚本安装主从双机热备自动切换解决方案 2013-07-02 19:24:13 标签:filesnginx keepalived 原创作品,允许转载,转载时请务必以超链 ...