CSP-S模拟15
不甘失败其实是个伪命题——如果我还未竭尽全力,那我应该毫无怨言;如果我已经竭尽全力,那我应该愿赌服输。
A. 网格图
暴力做法1:每次更新图,来一个bfs。
code
//正青春的年华,就是应该献给直指星辰的梦想啊!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 502;
const ll mod = 1e9 + 7;
int ans, res, c[maxn][maxn], d[maxn][maxn], n, k;
char s[maxn];
queue<pair<int, int> > q;
bool vis[maxn][maxn];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
void bfs(int x, int y)
{
q.push(make_pair(x, y));
while(!q.empty())
{
int n1 = q.front().first, n2 = q.front().second; q.pop();
if(vis[n1][n2]) continue;
vis[n1][n2] = 1; res++;
if(n1+1<=n && !vis[n1+1][n2] && d[n1+1][n2]==0) q.push(make_pair(n1+1, n2));
if(n2+1<=n && !vis[n1][n2+1] && d[n1][n2+1]==0) q.push(make_pair(n1, n2+1));
if(n1-1>=1 && !vis[n1-1][n2] && d[n1-1][n2]==0) q.push(make_pair(n1-1, n2));
if(n2-1>=1 && !vis[n1][n2-1] && d[n1][n2-1]==0) q.push(make_pair(n1, n2-1));
}
}
void change(int x, int y)
{
memcpy(d, c, sizeof(c));
for(int i=x; i<=x+k-1; i++)
{
for(int j=y; j<=y+k-1; j++)
{
d[i][j] = 0;
}
}
/*printf("New Change %d %d\n", x, y);
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++) printf("%d ", d[i][j]);
printf("\n");
}
printf("\n");*/
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++) vis[i][j] = 0;
}
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
if(d[i][j]==0 && !vis[i][j])
{
res = 0; bfs(i, j);
//printf("begin from: %d %d\n", i, j);
//printf("res = %d\n", res);
if(res > ans) ans = res;
}
}
}
}
int main()
{
n = read(); k = read();
for(int i=1; i<=n; i++)
{
scanf("%s", s+1);
for(int j=1; j<=n; j++)
{
if(s[j] == '.') c[i][j] = 0;
else c[i][j] = 1;
}
}
/*printf("Before Change:\n");
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++) printf("%d ", c[i][j]);
printf("\n");
}
printf("\n");*/
for(int i=1; i<=n-k+1; i++)
{
for(int j=1; j<=n-k+1; j++)
{
change(i, j);
}
}
printf("%d\n", ans);
return 0;
}
暴力做法2:预处理连通快并涂色,直接循环i, j找到和k*k的正方形相交(包括内部)的所有联通块,减去k*k内1的个数,再加上k*k。(以上都是50分)
code
//正青春的年华,就是应该献给直指星辰的梦想啊!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 502;
const ll mod = 1e9 + 7;
int ans, res, c[maxn][maxn], n, k, bel[maxn][maxn], val[maxn*maxn], col;
char s[maxn];
queue<pair<int, int> > q;
bool vis[maxn][maxn], ext[maxn*maxn];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
void bfs(int x, int y, int col)
{
q.push(make_pair(x, y));
while(!q.empty())
{
int n1 = q.front().first, n2 = q.front().second; q.pop();
if(vis[n1][n2]) continue;
vis[n1][n2] = 1; res++;
bel[n1][n2] = col;
if(n1+1<=n && !vis[n1+1][n2] && c[n1+1][n2]==0) q.push(make_pair(n1+1, n2));
if(n2+1<=n && !vis[n1][n2+1] && c[n1][n2+1]==0) q.push(make_pair(n1, n2+1));
if(n1-1>=1 && !vis[n1-1][n2] && c[n1-1][n2]==0) q.push(make_pair(n1-1, n2));
if(n2-1>=1 && !vis[n1][n2-1] && c[n1][n2-1]==0) q.push(make_pair(n1, n2-1));
}
}
void change(int x, int y)
{
//printf("From: %d %d\n", x, y);
int sum = 0;
for(int i=1; i<=col; i++) ext[i] = 0;
for(int i=x; i<=x+k-1; i++)
{
for(int j=y; j<=y+k-1; j++)
{
if(!ext[bel[i][j]])
{
sum += val[bel[i][j]], ext[bel[i][j]] = 1;
//printf("col %d is in\n", bel[i][j]);
//printf("upd: %d\n", val[bel[i][j]]);
}
if(c[i][j] == 0) sum--;
}
}
if(x-1>=1)
{
for(int j=y; j<=y+k-1; j++)
{
if(!ext[bel[x-1][j]])
{
sum += val[bel[x-1][j]], ext[bel[x-1][j]] = 1;
//printf("col %d is in\n", bel[x-1][j]);
//printf("upd: %d\n", val[bel[x-1][j]]);
}
}
}
if(x+k<=n)
{
for(int j=y; j<=y+k-1; j++)
{
if(!ext[bel[x+k][j]])
{
sum += val[bel[x+k][j]], ext[bel[x+k][j]] = 1;
//printf("col %d is in\n", bel[x+k][j]);
//printf("upd: %d\n", val[bel[x+k][j]]);
}
}
}
if(y-1>=1)
{
for(int i=x; i<=x+k-1; i++)
{
if(!ext[bel[i][y-1]])
{
sum += val[bel[i][y-1]], ext[bel[i][y-1]] = 1;
//printf("col %d is in\n", bel[i][y-1]);
//printf("upd: %d\n", val[bel[i][y-1]]);
}
}
}
if(y+k<=n)
{
for(int i=x; i<=x+k-1; i++)
{
if(!ext[bel[i][y+k]])
{
sum += val[bel[i][y+k]], ext[bel[i][y+k]] = 1;
//printf("col %d is in\n", bel[i][y+k]);
//printf("upd: %d\n", val[bel[i][y+k]]);
}
}
}
sum += k * k;
//printf("sum = %d\n", sum);
if(sum == 13) exit(0);
if(sum > ans) ans = sum;
}
int main()
{
//freopen("grid2.in", "r", stdin);
n = read(); k = read();
for(int i=1; i<=n; i++)
{
scanf("%s", s+1);
for(int j=1; j<=n; j++)
{
if(s[j] == '.') c[i][j] = 0;
else c[i][j] = 1;
}
}
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
if(!vis[i][j] && c[i][j] == 0)
{
col++; res = 0;
bfs(i, j, col); val[col] = res;
}
}
}
/*for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
printf("%d ", bel[i][j]);
}
printf("\n");
}
for(int i=1; i<=col; i++) printf("val[%d] = %d\n", i, val[i]);*/
for(int i=1; i<=n-k+1; i++)
{
for(int j=1; j<=n-k+1; j++)
{
change(i, j);
}
}
printf("%d\n", ans);
return 0;
}
正解的做法和莫队真的像极了,从排序到add和del再到“奇偶性优化”,所以上一次计算的东西依然有效,每次只修改一部分就行了。
code
//正青春的年华,就是应该献给直指星辰的梦想啊!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 502;
const int N = 250010;
const ll mod = 1e9 + 7;
int n, k, sum1, sum2, up, dn, lt, rt, ans, tot;
int id[maxn][maxn], sz[N], q[N][2], l, r, ck[N];
bool vis[maxn][maxn];
int bh[5] = {0, -1, 1, 0, 0};
int bl[5] = {0, 0, 0, -1, 1};
char s[maxn];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
inline void bfs(int xx, int yy)
{
l = 1, r = 0;
q[++r][0] = xx; q[r][1] = yy;
id[xx][yy] = ++tot; sz[tot] = 1;
int hg, le, x, y;
while(l <= r)
{
x = q[l][0], y = q[l][1];
l++;
for(int i=1; i<=4; i++)
{
hg = x + bh[i], le = y + bl[i];
if(hg <= 0 || le <= 0 || hg > n || le > n) continue;
if(vis[hg][le] && !id[hg][le])
{
id[hg][le] = tot;
q[++r][0] = hg; q[r][1] = le;
sz[tot]++;
}
}
}
}
inline void del(int x, int y)
{
ck[id[x][y]]--;
sum2--;
if(!ck[id[x][y]]) sum1 -= sz[id[x][y]];
}
inline void add(int x, int y)
{
if(!ck[id[x][y]]) sum1 += sz[id[x][y]];
ck[id[x][y]]++;
sum2++;
}
inline void pre()
{
for(int i=1; i<=k; i++)
{
for(int j=1; j<=k; j++) if(vis[i][j]) add(i, j);
}
for(int i=1; i<=k; i++)
{
if(vis[k+1][i]) add(k+1, i), dn++;
}
for(int i=1; i<=k; i++)
{
if(vis[i][k+1]) add(i, k+1), rt++;
}
ans = max(ans, sum1+k*k-sum2+dn+rt);
}
inline void workr(int x, int y)
{
int xx = x-k+1, yy = y-k+1;
if(xx > 1)
{
if(vis[xx-1][yy]) del(xx-1, yy), up--;
if(vis[xx-1][y+1]) add(xx-1, y+1), up++;
}
if(x < n)
{
if(vis[x+1][yy]) del(x+1, yy), dn--;
if(vis[x+1][y+1]) add(x+1, y+1), dn++;
}
lt = rt = 0;
for(int i=xx; i<=x; i++)
{
if(yy > 1 && vis[i][yy-1]) del(i, yy-1);
if(vis[i][yy]) lt++;
if(y <= n-2 && vis[i][y+2]) add(i, y+2), rt++;
}
ans = max(ans, sum1+k*k-sum2+up+dn+lt+rt);
}
inline void workl(int x, int y)
{
int xx = x-k+1, yy = y-k+1;
if(xx > 1)
{
if(vis[xx-1][y]) del(xx-1, y), up--;
if(vis[xx-1][yy-1]) add(xx-1, yy-1), up++;
}
if(x < n)
{
if(vis[x+1][y]) del(x+1, y), dn--;
if(vis[x+1][yy-1]) add(x+1, yy-1), dn++;
}
lt = rt = 0;
for(int i=xx; i<=x; i++)
{
if(y < n && vis[i][y+1]) del(i, y+1);
if(vis[i][y]) rt++;
if(yy >= 3 && vis[i][yy-2]) add(i, yy-2), lt++;
}
ans = max(ans, sum1+k*k-sum2+up+dn+lt+rt);
}
inline void workd(int x, int y)
{
int xx = x-k+1, yy = y-k+1;
if(yy > 1)
{
if(vis[xx][yy-1]) del(xx, yy-1), lt--;
if(vis[x+1][yy-1]) add(x+1, yy-1), lt++;
}
if(y < n)
{
if(vis[xx][y+1]) del(xx, y+1), rt--;
if(vis[x+1][y+1]) add(x+1, y+1), rt++;
}
up = dn = 0;
for(int i=yy; i<=y; i++)
{
if(xx > 1 && vis[xx-1][i]) del(xx-1, i);
if(vis[xx][i]) up++;
if(x <= n-2 && vis[x+2][i]) add(x+2, i), dn++;
}
ans = max(ans, sum1+k*k-sum2+up+dn+lt+rt);
}
int main()
{
n = read(); k = read();
for(int i=1; i<=n; i++)
{
scanf("%s", s+1);
for(int j=1; j<=n; j++)
{
if(s[j] == '.') vis[i][j] = 1;
}
}
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
if(vis[i][j] && !id[i][j]) bfs(i, j);
}
}
pre();
for(int i=k; i<=n; i++)
{
if((i-k+1)&1)
{
for(int j=k; j<n; j++) workr(i, j);
if(i < n) workd(i, n);
}
else
{
for(int j=n; j>k; j--) workl(i, j);
if(i < n) workd(i, k);
}
}
printf("%d\n", ans);
return 0;
}
B. 保险箱
有两个重要结论:1.若x是密码,gcd(x, n)也是密码,(直接输出n / gcd(x, n)可得34 pts)。
证明:学习同余方程时学到 a *x + b * y = gcd(x, y) 一定有解,设x是密码,所以x * k - n * c = gcd(x, n) 一定有解,所以x * k % n = gcd(x, n) 一定有解,由题意得 x * k % n 是密码,所以 gcd(x, n) 也是密码。
2.若x, y是密码,gcd(x, y)也是密码。
证明:设 x, y 是密码,那么 (p * x + q * y) % n 也是密码(p, q是任意正整数),因为a * x + b * y = gcd(x, y)一定有解,所以 a * x + b * y 和 gcd(x, y) 在 mod n 意义下同余一定有解。
因为 a * x + b * y 和 a * x + b * y + p * n + x + q * n * x 在 mod n 意义下同余,右边合并为 (a + p * n) * x + (b + q * n),它和 gcd(x, y) (mod n) 同余 一定有解,因为它是密码,所以 gcd(x, y) 也是密码。
设密码集合为A,A中所有数求 gcd 的结果为 x,所以A中所有数都是x的倍数,并且一定是连取的x, 2 * x, 3 * x……由结论2的 x 属于 A,而且是A中最小的数。反证法因为如果集合A中存在 y < x,那么gcd(x, y) < x,与假设不相符。
目的是让A最大化,所以要找到一个最小的x。m[k]一定在集合里,由1得 gcd(m[k], n) 一定在集合里,它一定是x的倍数,这可以初步确定x的范围。取gcd是为了取尽量小的范围。对于1 <= i < k,m[i]一定不在集合里,所以它一定不是x 的倍数,所以我们从 gcd(m[k], n) 的因数中删掉同时是 m[i] (1 <= i < k) 的数就好了。
因为只要找最小值,可以先判断可能更优再check,先把m排个序可以及时返回合法方案。
code
//正青春的年华,就是应该献给直指星辰的梦想啊!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 250005;
ll n, k, m[maxn];
inline ll read()
{
ll x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
ll gcd(ll a, ll b)
{
while(b^=a^=b^=a%=b);
return a;
}
bool check(ll x)
{
for(int i=k; i; i--)
{
if(m[i]%x == 0) return 0;
if(m[i] < x) break;
}
return 1;
}
int main()
{
n = read(); k = read();
for(ll i=1; i<=k; i++) m[i] = read();
ll gcc = gcd(m[k], n);
k--;
for(ll i=1; i<=k; i++) m[i] = gcd(gcc, m[i]);
sort(m+1, m+k+1);
k = unique(m+1, m+k+1)-m-1;
ll ans = gcc;
for(ll i=1; i<=sqrt(gcc); i++)
{
if(gcc % i == 0)
{
if(i<ans && check(i)) ans = i;
if(gcc/i<ans && check(gcc/i)) ans = gcc/i;
}
}
printf("%lld\n", n/ans);
return 0;
}
C. 追逐
直接鹤:来自%%%Chen_jr,都成洛古题解了%%%%%
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 4;
const int N = 9e4 + 3;
const int inf = 0x3f3f3f3f;
int n, m, f[maxn], sum;
ll ans, tmp[maxn][105][2], g[maxn];
struct node
{
int next, to;
}a[maxn<<1];
int head[maxn], len;
void add(int x, int y)
{
a[++len].to = y; a[len].next = head[x];
head[x] = len;
}
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
void solve(int x, int fa)
{
g[x] = 0;
for(int i=1; i<=sum; i++) tmp[x][i][0] = 0;
for(int i=head[x]; i; i=a[i].next)
{
int v = a[i].to;
if(v == fa) continue;
g[x] += f[v];
solve(v, x);
for(int j=1; j<=sum; j++) tmp[x][j][0] = max(tmp[x][j][0], max(tmp[v][j][1], tmp[v][j][0]));
}
for(int i=sum; i>=1; i--) tmp[x][i][1] = tmp[x][i-1][0] + g[x];
}
int rmx[maxn][405];
ll rval[maxn][105], rcm[maxn][105];
void ch(int x, int fa)
{
for(int i=1; i<=sum; i++) tmp[x][i][0] = max(tmp[x][i][0], max(tmp[fa][i][0], tmp[fa][i][1]));
for(int i=1; i<=sum; i++) tmp[x][i][1] = tmp[x][i-1][0] + g[x] + f[fa];
ans = max(ans, max(tmp[x][sum][0], tmp[x][sum][1]));
for(int i=1; i<=sum; i++) tmp[x][i][0] = max(tmp[fa][i][0], tmp[fa][i][1]);
for(int i=head[x]; i; i=a[i].next)
{
int v = a[i].to;
for(int k=1; k<=sum; k++)
{
ll val = 0;
//这个if else不是一样的吗??
if(v == fa) val = max(tmp[fa][k][0], tmp[fa][k][1]);
else val = max(tmp[v][k][0], tmp[v][k][1]);
if(val > rval[x][k])
{
rcm[x][k] = rval[x][k];
rval[x][k] = val;
rmx[x][k] = v;
}
else rcm[x][k] = max(rcm[x][k], val);
}
}
for(int i=head[x]; i; i=a[i].next)
{
int v = a[i].to;
if(v == fa) continue;
for(int j=1; j<=sum; j++) tmp[x][j][0] = rmx[x][j] == v ? rcm[x][j] : rval[x][j];
for(int j=1; j<=sum; j++) tmp[x][j][1] = tmp[x][j-1][0] + g[x] - f[v] + f[fa];
ch(v, x);
}
}
int main()
{
n = read(); sum = read();
for(int i=1; i<=n; i++) f[i] = read();
for(int i=1; i<n; i++)
{
int u = read(), v = read();
add(u, v); add(v, u);
}
if(n == 1 || sum == 0)
{
printf("0\n"); exit(0);
}
if(n == 2)
{
printf("%d\n", max(f[1], f[2]));
exit(0);
}
solve(1, 0);
ch(1, 0);
printf("%lld\n", ans);
return 0;
}
D. 字符串
没时间写博客就只能鹤……
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 4;
const int N = 9e4 + 3;
const int inf = 0x3f3f3f3f;
int n;
char c[maxn];
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
struct node
{
int pre, suf, mx, sum;
}t[maxn<<2];
struct tree
{
void pushup(int x)
{
int ls = x<<1, rs = x<<1|1;
t[x].sum = t[ls].sum + t[rs].sum;
t[x].pre = max(t[ls].pre, t[ls].sum + t[rs].pre);
t[x].suf = max(t[rs].suf, t[rs].sum + t[ls].suf);
t[x].mx = max(max(t[x].pre, t[x].suf), max(t[ls].mx, t[rs].mx));
t[x].mx = max(t[x].mx, t[ls].suf + t[rs].pre);
}
void build(int x, int l, int r)
{
if(l == r)
{
t[x].pre = t[x].suf = t[x].sum = t[x].mx = (c[l]=='C') ? 1 : -1;
if(t[x].mx < 0) t[x].mx = 0;
return;
}
int mid = (l + r) >> 1;
build(x<<1, l, mid);
build(x<<1|1, mid+1, r);
pushup(x);
}
node query(int x, int l, int r, int L, int R)
{
if(L <= l && r <= R) return t[x];
int mid = (l + r) >> 1;
if(L <= mid && R > mid)
{
node p = query(x<<1, l, mid, L, R);
node s = query(x<<1|1, mid+1, r, L, R);
node ans;
ans.sum = p.sum + s.sum;
ans.pre = max(p.pre, p.sum + s.pre);
ans.suf = max(s.suf, s.sum + p.suf);
ans.mx = max(max(ans.pre, ans.suf), max(p.mx, s.mx));
ans.mx = max(ans.mx, p.suf + s.pre);
return ans;
}
if(L <= mid) return query(x<<1, l, mid, L, R);
return query(x<<1|1, mid+1, r, L, R);
}
}T;
int main()
{
n = read();
scanf("%s", c+1);
T.build(1, 1, n);
int q = read();
while(q--)
{
int l = read(), r = read();
node ans = T.query(1, 1, n, l, r);
printf("%d\n", ans.mx-ans.sum);
}
return 0;
}