本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2023-12-23 22:25:15
md,G 题会做但是没调出来。
B
我认为解释不如直接看代码
cin>>a>>m>>l>>r;
if(a<l)
{
printf("%lld\n",calc(r)-calc(l-1));
}
else if(a>=l&&a<=r)
{
printf("%lld\n",(a-l)\/m+1+(r-a)\/m);
}
else
{
printf("%lld\n",calc2(l)-calc2(r+1));
}
C
以下结论我认为很显然,不做解释。
如果 $n$ 是偶数,那么排序,然后贪心地 $(1,2)$ 一对,$(3,4)$ 一对 $\cdots \cdots$
如果 $n$ 是奇数,那么枚举要删的点,经过预处理可以 $O(1)$ 计算得出删除每个点之后的答案。
D
纯纯前缀和二分板子。
E
先预处理连通块,然后枚举每个点,看把他涂成绿色的连通块个数,思想简单就是写起来麻烦。
F
暂时不会。
G
稍微修改一下求割点的算法,使其标记上它所连接的连通块数量,这个稍微思考一下就能想出来。
#include <bits\/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
const int maxm = 8e6 + 5;
int n, m;
int id(int i, int j)
{
return (i - 1) * m + j;
}
int head[maxn];
struct EDGE
{
int to, nxt;
} edge[maxm << 1];
int cnt = 1;
typedef long long ll;
const ll mod = 998244353;
ll qpow(ll a, ll b)
{
ll ret = 1;
while (b)
{
if (b & 1)
ret = ret * a % mod;
a = a * a % mod;
b >>= 1;
}
return ret;
}
ll inv(ll a)
{
return qpow(a, mod - 2);
}
void add(int u, int to)
{
edge[++cnt].to = to;
edge[cnt].nxt = head[u];
head[u] = cnt;
}
int dfn[maxn];
int low[maxn];
int cut[maxn];
int ids; \/\/ 时间戳
void print(int x)
{
int lie=x%m;
if(lie==0)lie=m;
printf("%d %d\n",(x-1)\/m+1,lie);
}
void tarjan(int u, int fa)
{
dfn[u] = low[u] = ++ids;
register int child = 0; \/\/ 子树
for (int i = head[u]; i; i = edge[i].nxt)
{
register int to = edge[i].to;
if (!dfn[to])
{ \/\/ 尚未访问过
tarjan(to, fa);
low[u] = min(low[u], low[to]);
if (low[to] >= dfn[u] && u != fa) \/\/ 需要经过u访问更早的祖先
{
cut[u]++;
}
if (u == fa) \/\/ 如果是根节点
{
child++; \/\/ 子树++
}
}
low[u] = min(low[u], dfn[to]); \/\/ 没有父子关系的话,那么按普通边计算,更新u
}
if (child >= 2 && u == fa) \/\/ 这样根节点也是割点,因为删除它就会有两棵蒲通的树不连通
{
cut[u] = child-1;
}
\/\/ printf("u = ",u);
\/\/ print(u);
\/\/ printf("cut = %d\n",cut[u]);
}
char s[1005][1005];
bool chk(int i, int j)
{
if (s[i][j] != '#')
return 0;
if (i < 1 || i > n)
return 0;
if (j < 1 || j > m)
return 0;
return 1;
}
bool td[1000005];
void dfs(int u)
{
td[u] = 1;
for (int i = head[u]; i; i = edge[i].nxt)
{
int to = edge[i].to;
if (!td[to])
dfs(to);
}
}
int ltkcnt;
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%s", s[i] + 1);
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (chk(i, j))
{
if (chk(i - 1, j))
{
add(id(i - 1, j), id(i, j));
add(id(i, j), id(i - 1, j));
}
if (chk(i + 1, j))
{
add(id(i + 1, j), id(i, j));
add(id(i, j), id(i + 1, j));
}
if (chk(i, j - 1))
{
add(id(i, j - 1), id(i, j));
add(id(i, j), id(i, j - 1));
}
if (chk(i, j + 1))
{
add(id(i, j + 1), id(i, j));
add(id(i, j), id(i, j + 1));
}
}
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (s[i][j] == '#' && !dfn[id(i, j)])
{
tarjan(id(i, j), id(i, j));
}
if (s[i][j] == '#' && !td[id(i, j)])
{
dfs(id(i, j));
ltkcnt++;
}
}
}
\/\/ printf("ltkcnt = %d\n", ltkcnt);
ll ans = 0;
ll ansct = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (s[i][j] != '.')
{
ansct++;
bool ok=0;
ok|=(s[i-1][j]=='#');
ok|=(s[i][j-1]=='#');
ok|=(s[i+1][j]=='#');
ok|=(s[i][j+1]=='#');
if(!ok)ans+=ltkcnt-1;
else ans += (ltkcnt + max(0, cut[id(i, j)]));
\/\/ printf("%d %d %d\n", i, j, (ltkcnt + cut[id(i, j)]));
ans %= mod;
}
}
}
ans *= inv(ansct);
printf("%lld", ans % mod);
return 0;
}

鲁ICP备2025150228号