A 美丽的路径
题目描述
叶妹妹非常喜欢图论题,这天她出了一个图论题,有一个nn个点mm条边的无向图,其中第ii个点的点权为 ,她定义一条点数为
路径:
,
,...,
;其中点
与点
通过一条边直接相连
,所以路径中可以出现重复的点。她将一条路径的美丽值定义为:假设这条路径的点数为
,那么这条路径的美丽值就是此路径上的所有点的点权中第
小的点权。现在她会询问你是否存在起点为
号点并且终点为
号点的路径,如果存在则先输出
,再输出存在的所有路径中的最大的美丽值;否则直接输出
。
输入描述:
有多组样例,第一行输入一个数TT,表述样例组数
对于每组样例,第一行先输入四个数 ,保证
第二行输入 个数,其中第
个数为
,表示第
个点的点权
接下来输入 行,第
行输入两个数
和
,表示点
与点
之间有一条无向边,保证
【数据规模与约定】
输出描述:
如果存在起点为 号点并且终点为
号点的路径,则第一行输出
,第二行输出存在的所有的路径中的最大的美丽值;否则直接输出
分析:
首先,如果不存在从 到
的路径,则直接输出
.
否则,二分最大美丽值,问题就变成了询问是否存在从 到
的路径,且这条路径点权的中位数大于等于
.
在 到
的路径中,如果存在两个相邻的点且他们的点权都大于等于
,那么我们可以在这两个点之间来回走,使得最终路径的中位数大于等于
.
在 到
的路径中,如果不存在两个相邻的点且他们的点权都大于等于
,那为了使得路径美丽值大于等于
,最终路径一定是 “点权大于等于
的点” 与 “点权小于
的点” 交替走,且起点终点的点权不能同时小于
.
代码实现:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
const int M = (int)2e5;
const int N = (int)2e2;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)998244353;
int n, m, s, t;
int cnt, head[M + 5];
struct enode
{
int v, nx;
} Edge[M * 2 + 5];
int a[M + 5];
bool vis[M + 5];
bool vis2[M + 5];
void init()
{
cnt = 0;
for(int i = 1; i <= n; ++i)
{
head[i] = -1;
vis[i] = 0;
}
}
void add(int u, int v)
{
Edge[cnt].v = v;
Edge[cnt].nx = head[u];
head[u] = cnt++;
}
void dfs1(int u)
{
for(int i = head[u]; ~i; i = Edge[i].nx)
{
int v = Edge[i].v;
if(vis[v]) continue;
vis[v] = 1;
dfs1(v);
}
}
void dfs2(int u, int mid)
{
for(int i = head[u]; ~i; i = Edge[i].nx)
{
int v = Edge[i].v;
if(vis2[v]) continue;
if(a[u] >= mid && a[v] < mid || a[u] < mid && a[v] >= mid)
{
vis2[v] = 1;
dfs2(v, mid);
}
}
}
bool check(int mid)
{
for(int i = 1; i <= n; ++i)
{
if(vis[i] && a[i] >= mid)
{
for(int j = head[i]; ~j; j = Edge[j].nx)
{
if(a[Edge[j].v] >= mid) return 1;
}
}
}
if(a[s] < mid && a[t] < mid) return 0;
for(int i = 1; i <= n; ++i) vis2[i] = 0;
vis2[s] = 1; dfs2(s, mid);
return vis2[t];
}
int read()
{
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch))
{
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
void work()
{
n = read(); m = read(); s = read(); t = read(); init();
for(int i = 1; i <= n; ++i) a[i] = read();
for(int i = 1, u, v; i <= m; ++i)
{
u = read(); v = read();
add(u, v), add(v, u);
}
vis[s] = 1; dfs1(s);
if(!vis[t]) {printf("NO\n"); return;}
int l = 1, r = (int)1e9, mid;
while(l < r)
{
mid = (l + r + 1) >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
printf("YES\n%d\n", r);
}
int main()
{
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
int T; scanf("%d", &T);
while(T--) work();
// work();
// cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
} B 比武招亲(上)
题目描述
众所周知,天姐姐只喜欢天下最聪明的人,为了找到这样的人,她决定比武招亲!
只见天姐姐在榜上留下了这样一道问题,谁做出来了就可以俘获她的芳心!
爱慕天姐姐已久的泽鸽鸽问询赶来,只见榜上写着:
给定 n,mn,mn,m,定义一种序列,构造方法如下:
在
中任意选择
次,得到了
个整数(显然数字可能相同);
将选出的
个数字排序之后得到一个序列
。
定义一个序列的贡献为
,求所有本质不同的序列的贡献和。
为了防止结果过大,将答案为
取模后输出。
(对于两个序列长度为m的序列 A、B,若
,则序列 A、B 本质不同)
泽鸽鸽心有余而力不足,而你作为他最好的基友决定帮助泽鸽鸽俘获美人心!
现在,这个重任就交给你啦!
输入描述:
一行输入两个正整数 n,m
【数据规模与约定】
1 <= n, m <= 5*10^5
输出描述:
一行一个整数,为答案对 998244353 取模后的结果。
分析:
计算每个数的贡献.
当 作为最大值时,有
种方案.
当 作为最小值时,有
种方案.
因此答案为 .
代码实现:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
const int M = (int)5e5;
const int N = (int)1e6;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)998244353;
int n, m;
int a[M + 5];
ll fac[N + 5];
ll quick(ll a, ll b, ll p = mod)
{
ll s = 1;
while(b)
{
if(b & 1) s = s * a % p;
a = a * a % p;
b >>= 1;
}
return s;
}
ll inv(ll n, ll p = mod)
{
return quick(n, p - 2, p);
}
ll C(int n, int m)
{
return fac[n] * inv(fac[m]) % mod * inv(fac[n - m]) % mod;
}
ll S(int k, int r)
{
return C(k + r - 1, k - 1);
}
void work()
{
scanf("%d %d", &n, &m);
ll ans = 0;
for(int i = 1; i <= n; ++i) ans = (ans + 1ll * i * S(i, m - 1)) % mod;
for(int i = 1; i <= n; ++i) ans = (ans - 1ll * i * S(n - i + 1, m - 1)) % mod;
ans = (ans % mod + mod) % mod;
printf("%lld\n", ans);
}
int main()
{
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
// int T; scanf("%d", &T);
// while(T--) work();
fac[0] = 1; for(int i = 1; i <= N; ++i) fac[i] = fac[i - 1] * i % mod;
work();
// cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
} C 比武招亲(下)
题目描述
泽鸽鸽在你的帮助下成功做出了天姐姐出的第一个问题,只见天姐姐又问道:
序列长度为n,我们要按顺序填数,能填的数的范围在 [1,m],
一个序列的贡献为序列中元素的最大值,求所有本质不同的序列的贡献和对一个质数p取模后的值。
(对于两个序列 A、B,若,则序列 A、B 本质不同)
泽鸽鸽灵光一现!花了1s 就秒掉了这题。
只见天姐姐也灵光一现!突然又冒出了一个神奇的想法:
令
天姐姐说如果能做出这道题,她就愿意和泽鸽鸽在一起!
这可苦恼了泽鸽鸽,而你作为他最好的基友决定帮助泽鸽鸽俘获美人心!
现在,这个重任就交给你啦!
输入描述:
一行输入两个正整数 n, p
【数据规模与约定】
1 <= n <= 10^5, n < p < 10^9+7,保证 p 为质数
输出描述:
一行一个数表示答案
分析:
先考虑题目给的第一个问题
答案为 ,
次多项式的前缀和为
次多项式.
记 ,题目即求
.
于是 .
因此只需要求 ,其中
可以使用欧拉降幂求得.
之后我们可以求出 ,再预处理出阶乘和逆元可以通过拉格朗日差值
得到
.
代码实现:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
const int M = (int)1e5;
const int N = (int)2e2;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)998244353;
int n, p;
int f[M + 5];
int pre[M + 5];
int suf[M + 5];
int fac[M + 5];
int invfac[M + 5];
inline int Mod1(ll n, const int& p)
{
return n % p;
}
inline int Mod2(ll n, const int& p)
{
return (n % p + p) % p;
}
inline int Mod3(ll n, const int& p)
{
return n < p ? n : n % p + p;
}
int quick1(int a, int b, int p)
{
int s = 1;
while(b)
{
if(b & 1) s = Mod1(1ll * s * a, p);
a = Mod1(1ll * a * a, p);
b >>= 1;
}
return s;
}
int quick3(int a, int b, int p)
{
int s = 1;
while(b)
{
if(b & 1) s = Mod3(1ll * s * a, p);
a = Mod3(1ll * a * a, p);
b >>= 1;
}
return s;
}
int inv(int n, int p)
{
return quick1(n, p - 2, p);
}
int phi(int n)
{
int ans = n;
for(int i = 2; i * i <= n; ++i)
{
if(n % i == 0)
{
ans = ans / i * (i - 1);
while(n % i == 0) n /= i;
}
}
if(n > 1) ans = ans / n * (n - 1);
return ans;
}
int calc(int n, int p)
{
if(p == 1) return Mod3(n, p);
return quick3(n, calc(n, phi(p)), p);
}
int lang(int m)
{
for(int i = 1; i <= n + 2; ++i)
{
f[i] = Mod2(f[i - 1] + 1ll * i * (quick1(i, n, p) - quick1(i - 1, n, p)), p);
}
if(m <= n + 2) return Mod1(f[m], p);
pre[1] = 1; fac[0] = invfac[0] = 1; fac[1] = 1;
for(int i = 2; i <= n + 2; ++i)
{
pre[i] = Mod1(1ll * pre[i - 1] * (m - i + 1), p);
fac[i] = Mod1(1ll * fac[i - 1] * i, p);
}
suf[n + 2] = 1; invfac[n + 2] = inv(fac[n + 2], p);
for(int i = n + 1; i >= 1; --i)
{
suf[i] = Mod1(1ll * suf[i + 1] * (m - i - 1), p);
invfac[i] = Mod1(1ll * invfac[i + 1] * (i + 1), p);
}
int ans = 0, up, dn;
for(int i = 1; i <= n + 2; ++i)
{
up = Mod1(1ll * Mod1(1ll * pre[i] * suf[i], p) * f[i], p);
dn = Mod1(((n + 2 - i) & 1 ? -1ll : 1ll) * invfac[i - 1] * invfac[n + 2 - i], p);
ans = Mod1(1ll * ans + Mod1(1ll * up * dn, p), p);
}
ans = Mod2(ans, p);
return ans;
}
void work()
{
scanf("%d %d", &n, &p);
int m = calc(n, p) % p;
printf("%d\n", lang(m));
}
int main()
{
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
// int T; scanf("%d", &T);
// while(T--) work();
work();
// cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
} D 石子游戏
题目描述
叶妹妹很喜欢玩石头,于是这天泽鸽鸽给她出了一道石子游戏,规则是这样的:有 堆石子排成一行,其中第
堆石子有
个,叶妹妹可以选择做无数次这种操作:每次操作把连续相邻的
个石子堆中的每堆石子数目加一,请问叶妹妹能否让每堆石子的数目都相同呢?叶妹妹觉得这题太简单了,于是丢给了聪明的你,快来解决这个问题吧!
输入描述:
第一行输入样例组数
对于每组样例来说,第一行输入两个数 和
第二行输入 个数,其中第
个数为
【数据规模与约定】
输出描述:
输出总共 行,对于每组样例来说,如果能使每堆石子的数目都相同则输出一个整数
,
表示达到相同时的最少的操作数;否则输出
分析:
记差分数组 .
让每堆石子数目相同就等价于 全
.
随之对应的,每次操作就相当于让 .
因此只需要从前往后扫一遍 .
若当前 ,则给
这段区间加上
,前提是
.
若当前 ,则给
这段区间加上
,前提是
.
代码实现:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
const int M = (int)1e5;
const int N = (int)1e6;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)998244353;
int n, k;
int a[M + 5];
ll b[M + 5];
/**
2
5 3
1 2 1 2 3
5 1
2 1 1 2 3
**/
void work()
{
scanf("%d %d", &n, &k);
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for(int i = 2; i <= n; ++i) b[i] = a[i] - a[i - 1];
ll ans = 0;
for(int i = 2; i <= n; ++i)
{
if(b[i] < 0)
{
if(i + k > n + 1)
{
printf("-1\n");
return;
}
ans -= b[i];
b[i + k] += b[i];
b[i] = 0;
}
else if(b[i] > 0)
{
if((i - 1) % k)
{
printf("-1\n");
return;
}
ans += (i - 1) / k * b[i];
b[i] = 0;
}
}
printf("%lld\n", ans);
}
int main()
{
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
int T; scanf("%d", &T);
while(T--) work();
// work();
// cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
} E 树上博弈
题目描述
叶妹妹非常喜欢在树上玩博弈游戏,这天叶妹妹和泽鸽鸽遇到了一个有趣的树上博弈游戏。
游戏规则是:给定一颗有 个点的无根树,第
个点的权值为
,叶妹妹与泽鸽鸽轮流执行操作。
操作如下:
选择一个度数为11的节点(即叶子节点),自己的得分加上该点的权值
删去这个节点以及该节点的所有邻接的边
当树上没有点了则表示游戏结束,现在规定由叶妹妹先执行操作,双方都想让自己得分与对方得分的差值尽可能大(即尽可能让自己多得分,并且尽可能让对方少得分),假设叶妹妹和泽鸽鸽都以最优的策略玩这个游戏,求游戏结束时叶妹妹与泽鸽鸽分数的差值是多少?
输入描述:
第一行输入一个整数 ,表示样例的组数
每组样例的第一行输入一个整数
第二行输入 个整数,第
个数为
,表示第
个点的点权为
接下来输入 行,每行输入两个整数
,
,表示树上有一条从点
到点
的无向边
保证输入一定是一棵树
【数据规模与约定】
输出描述:
输出 行,每行输出一个数字
,
表示游戏结束叶妹妹与泽鸽鸽分数的差值
分析:
赛时用记忆化搜索 + 卡常艹过去了.
改成递推就快很多了.
代码实现:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
const int M = (int)20;
const int N = (int)1e6;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)998244353;
int n, m;
int a[M];
int con[M];
int cnt[1<<M];
int one[1<<M][M], oneLen[1<<M];
ll f[1<<M];
inline bool valid(const int& i)
{
int c = 2 * cnt[i];
for(int j = 0; j < oneLen[i] && one[i][j] < n; ++j)
{
c -= cnt[i & con[one[i][j]]];
}
return c == 2;
}
void work()
{
scanf("%d", &n); m = (1<<n);
for(int i = 0; i < n; ++i) scanf("%d", &a[i]), con[i] = 0;
for(int i = 2, u, v; i <= n; ++i)
{
scanf("%d %d", &u, &v);
--u, --v;
con[u] |= (1<<v), con[v] |= (1<<u);
}
f[0] = 0;
for(int i = 1; i < m; ++i)
{
if(!valid(i)) continue;
if(n - cnt[i] & 1) //泽哥哥
{
f[i] = inf;
for(int j = 0, p, k; j < oneLen[i]; ++j)
{
p = one[i][j], k = (i&con[p]);
if(!(k & k-1))
{
f[i] = min(f[i], f[i^(1<<p)] - a[p]);
}
}
}
else
{
f[i] = -inf;
for(int j = 0, p, k; j < oneLen[i]; ++j)
{
p = one[i][j], k = (i&con[p]);
if(!(k & k-1)) f[i] = max(f[i], f[i^(1<<p)] + a[p]);
}
}
}
printf("%lld\n", f[m - 1]);
}
int main()
{
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
for(int i = 1; i < (1<<M); ++i)
{
cnt[i] = cnt[i&(i - 1)] + 1;
for(int j = 0; j < M; ++j)
{
if(i>>j&1) one[i][oneLen[i]++] = j;
}
}
int T; scanf("%d", &T);
while(T--) work();
// work();
return 0;
} F 我的心是冰冰的
题目描述
泽鸽鸽很喜欢王冰冰,为了证明他是否配得上冰冰,叶妹妹出了一道题来考他:给定了一棵有 个点的树,你需要对树的每个点进行染色,且要求每两个相邻(即有边相连)的点颜色不同,叶妹妹想知道至少需要拥有多少种不同的颜色才能完成这种染色?泽鸽鸽觉得这题太简单了,于是聪明的你快来解答吧!
输入描述:
第一行输入一个整数 ,表示样例的组数
每组样例的第一行输入一个整数
接下来输入 行,每行输入两个整数
和
,表示树上有一条从点
到点
的无向边
保证输入一定是一棵树
【数据规模与约定】
输出描述:
输出 行,每行输出一个整数
,
表示至少需要的颜色种类数
分析:
若 ,则答案为
.
否则,答案为 .
代码实现:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
const int M = (int)1e5;
const int N = (int)5e5;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)1e9 + 7;
int n;
int out[M + 5];
void work()
{
scanf("%d", &n);
for(int i = 1; i <= n - 1; ++i) scanf("%*d %*d");
if(n == 1) printf("1\n");
else printf("2\n");
}
int main()
{
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
int T; scanf("%d", &T);
while(T--) work();
// work();
// cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
} G 模仿游戏
题目描述
有一天叶妹妹和泽鸽鸽他们一起参加一个模仿游戏,这个游戏主要任务是打怪兽,每个怪兽都有一个权值,如果两个怪兽的权值相同,那么表示这两个怪兽是同一类型的怪兽。对于任何一个怪兽,只能由一个人去打败它,并且一个人无法同时打两只及两只以上的怪兽。泽鸽鸽和叶妹妹打败任何一个怪兽都只需要花费一分钟,并且他们只能在整数分钟去打怪兽,不能出现小数分钟。由于这是模仿游戏,因此叶妹妹只能模仿泽鸽鸽去打败怪兽。比如,当泽鸽鸽在之前已经打败了权值为 的怪兽后,叶妹妹才能去打权值为
的怪兽(即相同类型的怪兽)。现在给出每个怪兽的出现时间和权值,请你设计一个最优的顺序使得所有怪兽都被打败后的时间最早!
输入描述:
第一行输入一个数 ,表示样例组数
对于每组样例,第一行先输入一个数
接下来输入 行,每行输入两个数
和
表示第
个怪兽出现的时间,
表示第
个怪兽的权值
【数据规模与约定】
输出描述:
输出 行,每行输出一个数
,表示最优顺序下所有怪兽都被打败后的最早时间
分析:
维护优先队列 和 队列
.
存的是只能被泽哥哥打的怪兽,按照下一只同类怪兽的出现时间排序.
存的是可以被叶妹妹打的怪兽.
每回合,尽量让泽哥哥去打 的怪兽.
详见代码.
代码实现:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
const int M = (int)1e5;
const int N = (int)2e2;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)998244353;
int read()
{
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch))
{
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int n;
struct node
{
int a, b;
bool operator<(const node& b)const
{
return a < b.a;
}
} s[M + 5];
int nx[M + 5];
int cnt[M + 5];
bool vis[M + 5];
struct qnode
{
int b, nx;
qnode(int _b = 0, int _nx = 0): b(_b), nx(_nx){}
bool operator<(const qnode& b)const
{
return nx > b.nx;
}
};
priority_queue<qnode> q1;
queue<qnode> q2;
void work()
{
n = read();
for(int i = 1; i <= n; ++i) s[i].a = read(), s[i].b = read();
sort(s + 1, s + n + 1);
memset(nx, -1, sizeof(nx));
memset(cnt, 0, sizeof(cnt));
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; ++i)
{
if(nx[s[i].b] == -1) nx[s[i].b] = M + 1;
else if(nx[s[i].b] == M + 1) nx[s[i].b] = s[i].a;
}
for(int i = 1, p = 1; i <= 2 * M; ++i)
{
while(p <= n && s[p].a <= i)
{
if(vis[s[p].b]) q2.push(qnode());
else
{
if(!cnt[s[p].b]) q1.push(qnode(s[p].b, nx[s[p].b]));
++cnt[s[p].b];
}
++p;
}
if(!q2.empty()) q2.pop();
if(!q1.empty())
{
int b = q1.top().b; q1.pop();
vis[b] = 1;
while(--cnt[b]) q2.push(qnode());
}
else if(!q2.empty()) q2.pop();
if(p == n + 1 && q1.empty() && q2.empty())
{
printf("%d\n", i);
return;
}
}
}
int main()
{
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
int T; scanf("%d", &T);
while(T--) work();
// work();
// cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
} H 白色长方形
咕咕咕
I 正十字
题目描述
叶妹妹最近遇到了一个简单题,在一个 的网格中,每个格子的权值要么是
,要么是
。
表示这个格子为空,可以停留或者经过这个格子;
表示这个格子有障碍物,无法停留也无法经过这个格子。现在定义一个边长为
的正十字是以一个格子为中心,然后这个格子连续向上,向下,向左,向右都连续拓展
个格子,这
格子都属于这个边长为
的正十字,并且正十字里面的所有格子权值都必须为
。我们规定正十字能经过一个点,表示这个正十字的中心能经过这个点。在不越界和不遇到障碍物的情况下,正十字可以通过上下左右四个方向进行平移。现在有
个询问,每次给你两个格子的坐标
和
,请问以
为起点并且能通过平移到达
的正十字的边长最大是多少呢?如果无法到达则输出
(正十字一定要经过起点,
输入描述:
第一行输入 个数,
,
,
接下来 行,每行输入
个数,第
行第
个数为
,表示网格中第
行第
列的格子的权值为
接下来 行,每行输入四个数,
,表示询问的两个格子的坐标
和
【数据规模与约定】
输出描述:
输出总共 行,每行输出一个数
,如果无法到达
,否则
表示以
为起点并且能通过平移到达
的正十字的最大边长
分析:
处理出 ,表示点
所能承受的最大正十字大小.
之后倒序枚举最大正十字大小,再枚举询问,用并查集维护连通性.
代码实现:
#include <cstdio>
#include <vector>
#include <ctype.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
const int M = (int)1e3;
const int N = (int)1e5;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)998244353;
int n, m, q;
int s[M + 5][M + 5];
vector< pair<int, int> > v[M + 5];
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
int x1[N + 5], y1[N + 5];
int x2[N + 5], y2[N + 5];
int fa[M * M + 5];
int ans[N + 5];
bool vis[M + 5][M + 5];
int read()
{
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch))
{
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
void print(int n)
{
if(n < 0)
{
putchar('-');
print(-n);
}
else
{
if(n > 9) print(n / 10);
putchar(n % 10 + '0');
}
}
inline int getS(const int& x1, const int& y1, const int& x2, const int& y2)
{
return s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1];
}
inline int id(const int& x, const int& y)
{
return (x - 1) * m + y;
}
int calc(const int& x1, const int& y1, const int& d)
{
int l = 0, r = M, mid, x2, y2, mix, miy, mxx, mxy;
while(l < r)
{
mid = (l + r + 1) >> 1;
x2 = x1 + mid * dx[d], y2 = y1 + mid * dy[d];
mix = min(x1, x2), miy = min(y1, y2);
mxx = max(x1, x2), mxy = max(y1, y2);
if(x2 < 1 || x2 > n || y2 < 1 || y2 > m || getS(mix, miy, mxx, mxy)) r = mid - 1;
else l = mid;
}
return r;
}
int tofind(int x)
{
if(x == fa[x]) return x;
return fa[x] = tofind(fa[x]);
}
void work()
{
n = read(); m = read(); q = read();
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
fa[id(i, j)] = id(i, j);
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + read();
}
}
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
if(getS(i, j, i, j) == 1) continue;
int mi = inf;
for(int k = 0; k < 4; ++k) mi = min(mi, calc(i, j, k));
v[mi].push_back(make_pair(i, j));
}
}
for(int i = 1; i <= q; ++i)
{
ans[i] = -1;
x1[i] = read(); y1[i] = read();
x2[i] = read(); y2[i] = read();
}
for(int i = M; i >= 0; --i)
{
for(auto& p: v[i])
{
vis[p.first][p.second] = 1;
for(int k = 0; k < 4; ++k)
{
int x = p.first + dx[k], y = p.second + dy[k];
if(x < 1 || x > n || y < 1 || y > m || !vis[x][y]) continue;
int u = tofind(id(p.first, p.second)), v = tofind(id(x, y));
if(u != v) fa[u] = v;
}
}
for(int j = 1; j <= q; ++j)
{
if(~ans[j]) continue;
if(vis[x1[j]][y1[j]] && vis[x2[j]][y2[j]]
&& tofind(id(x1[j], y1[j])) == tofind(id(x2[j], y2[j]))) ans[j] = i;
}
}
for(int i = 1; i <= q; ++i) print(ans[i]), putchar('\n');
}
int main()
{
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
// int T; scanf("%d", &T);
// while(T--) work();
work();
// cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
} J 桥
咕咕咕

京公网安备 11010502036488号