2023 省选联测41 - 43
2023 省选联测41
A. 冤家路窄
找出 \(Dag\) 用总路径数减去相遇的路径数
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, int> pli;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 2e5 + 55, mod = 1e9 + 7;
int n, m, S, T, head[maxn], tot;
struct edge{int to, net, val;}e[maxn << 1 | 1];
void add(int u, int v, int w){
e[++tot] = {v, head[u], w};
head[u] = tot;
}
void add(int &x, int y){x += y; if(x >= mod) x -= mod;}
ll diss[maxn], dist[maxn]; bool vis[maxn];
priority_queue<pli, vector<pli>, greater<pli>>Q;
vector<int>g1[maxn], g2[maxn];
int f[maxn][2], deg1[maxn], deg2[maxn];
queue<int>q;
int main(){
freopen("avoid.in","r",stdin);
freopen("avoid.out","w",stdout);
n = read(), m = read(); S = read(); T = read();
for(int i = 1; i <= m; ++i) {
int u = read(), v = read(), w = read();
add(u, v, w); add(v, u, w);
}
memset(diss, 0x3f, sizeof(diss));
diss[S] = 0; Q.push(pli(0, S));
while(!Q.empty()){
int x = Q.top().second; Q.pop();
if(vis[x])continue; vis[x] = true;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(diss[v] > diss[x] + e[i].val){
diss[v] = diss[x] + e[i].val;
Q.push(pli(diss[v], v));
}
}
}
memset(dist, 0x3f, sizeof(dist));
memset(vis, 0, sizeof(vis));
dist[T] = 0; Q.push(pli(0, T));
while(!Q.empty()){
int x = Q.top().second; Q.pop();
if(vis[x])continue; vis[x] = true;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(dist[v] > dist[x] + e[i].val){
dist[v] = dist[x] + e[i].val;
Q.push(pli(dist[v], v));
}
}
}
for(int i = 1; i <= tot; i += 2){
int u = e[i].to, v = e[i + 1].to;
if(diss[u] > diss[v])swap(u, v);
if(diss[u] + dist[v] + e[i].val == diss[T]){
g1[u].push_back(v); ++deg1[v];
g2[v].push_back(u); ++deg2[u];
}
}
f[S][0] = 1; q.push(S);
while(!q.empty()){
int x = q.front(); q.pop();
for(int v : g1[x]){
--deg1[v]; if(deg1[v] == 0)q.push(v);
add(f[v][0], f[x][0]);
}
}
f[T][1] = 1; q.push(T);
int ans = 1ll * f[T][0] * f[T][0] % mod;
while(!q.empty()){
int x = q.front(); q.pop();
if(diss[x] == dist[x])add(ans, mod - 1ll * f[x][1] * f[x][0] % mod * f[x][1] % mod * f[x][0] % mod);
for(int v : g2[x]){
--deg2[v]; if(deg2[v] == 0)q.push(v);
if(max(diss[v], dist[x]) < min(diss[x], dist[v]))
add(ans, mod - 1ll * f[x][1] * f[v][0] % mod * f[x][1] % mod * f[v][0] % mod);
add(f[v][1], f[x][1]);
}
}
printf("%d\n",ans);
return 0;
}
B. 夹克姥爷win了win了
结论是 \(k! + k\) 证明用到 \(HAll\) 定理啥的
\(C_{n}^{k} <= P_{n}^{k - 1}\)
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 1e5 + 55, base = 10000;
struct big{
int a[maxn];
void init(int x){while(x){a[++a[0]] = x % base; x /= base;}}
void mul(int x){
int res = 0, tmp;
for(int i = 1; i <= a[0]; ++i){
tmp = a[i] * x + res;
a[i] = tmp % base;
res = tmp / base;
}
while(res){a[++a[0]] = res % base; res /= base;}
}
void add(int x){
int res = x, now = 0;
while(res){
++now;
a[now] += res;
res = a[now] / base;
a[now] %= base;
}
a[0] = max(a[0], now);
}
void print(){
printf("%d",a[a[0]]);
for(int i = a[0] - 1; i >= 1; --i)printf("%04d",a[i]);
printf("\n");
}
}a;
int main(){
freopen("win.in","r",stdin);
freopen("win.out","w",stdout);
int k; cin >> k;
if(k == 1){
printf("-1\n");
return 0;
}
a.init(1);
for(int i = 2; i <= k; ++i)a.mul(i);
a.add(k);
a.print();
return 0;
}
C. 39与93
\(sort\) 后,每次只需枚举到与 \(b\) 位数相同的部分,复杂度是正确的
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 5e5 + 55, mod = 1e9 + 7;
int n, s, b[maxn], sum;
int mp[13][maxn * 4], tmp[13][maxn * 4];
void add(int &x, int y){x += y; if(x >= mod) x -= mod;}
int main(){
freopen("stone.in","r",stdin);
freopen("stone.out","w",stdout);
n = read(), s = read();
for(int i = 1; i <= n; ++i)b[i] = read(), sum ^= b[i];
sort(b + 1, b + n + 1);
mp[0][0] = 1;
for(int l = 1; l <= n; ++l){
int up = 1; while(up <= b[l]) up <<= 1; --up;
for(int i = 0; i < s; ++i){
for(int j = 0; j <= up; ++j)if(mp[i][j])
add(tmp[(i + 1) % s][j xor b[l]], mp[i][j]),
add(tmp[i][j], mp[i][j]);
}
for(int i = 0; i < s; ++i){
for(int j = 0; j <= up; ++j)mp[i][j] = tmp[i][j], tmp[i][j] = 0;
}
}
add(mp[n % s][sum], mod - 1);
printf("%d\n", mp[0][sum]);
return 0;
}
D. Zbox的刷题I
把贡献分开算,变成算期望轮数和操作数
gtm巨佬的题解
https://www.luogu.com.cn/blog/663705/xing-xuan-lian-ce-41-post
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int mod = 998244353, maxn = 2e6 + 55;
int qpow(int x, int y){
int ans = 1;
for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
return ans;
}
int pp[maxn], pq[maxn], f[maxn], fac[maxn], ifac[maxn];
int n, p, q, a, b;
int c(int n, int m){return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod;}
int main(){
freopen("exercise.in","r",stdin);
freopen("exercise.out","w",stdout);
n = read(), p = read(), q = read(), a = read(), b = read();
p = 1ll * p * qpow(q, mod - 2) % mod; q = (mod + 1 - p) % mod;
pp[0] = fac[0] = ifac[0] = 1;
for(int i = 1; i <= n; ++i)pp[i] = 1ll * pp[i - 1] * p % mod;
for(int i = 0; i <= n; ++i)pq[i] = qpow((mod + 1 - pp[i]) % mod, mod - 2);
for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
ifac[n] = qpow(fac[n], mod - 2);
for(int i = n - 1; i >= 1; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
int ans = 0;
for(int i = 1; i <= n; ++i){
int res = 1ll * c(n, i) * pq[i] % mod;
if(i & 1)ans = (ans + res) % mod;
else ans = (ans - res + mod) % mod;
}
ans = 1ll * ans * b % mod;
ans = (ans + 1ll * a * n % mod * qpow(q, mod - 2) % mod) % mod;
printf("%d\n",ans);
return 0;
}
2023 省选联测42
A. 猜数字
直接 \(hash\) ,找个好模数
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int maxn = 4e5 + 55, mod = 1004535809;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = (x * 10ll + (c ^ 48)) % mod; c = getchar();}while(isdigit(c));
return x;
}
map<int, int>mp;
int qpow(int x, int y){
int ans = 1;
for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
return ans;
}
int main(){
freopen("guess.in","r",stdin);
freopen("guess.out","w",stdout);
for(int i = 1; i <= 50000; ++i)mp[qpow(i, i)] = i;
printf("%d\n",mp[read()]);
return 0;
}
B. 吵架
套路线段树维护最远点对
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 1e5 + 55;
int n, q;
vector<int>g[maxn];
int si[maxn], fa[maxn], dep[maxn], top[maxn], son[maxn];
void dfs1(int x){
si[x] = 1;
for(int v : g[x])if(v != fa[x]){
dep[v] = dep[x] + 1;
fa[v] = x; dfs1(v);
si[x] += si[v];
if(si[son[x]] < si[v])son[x] = v;
}
}
void dfs2(int x, int tp){
top[x] = tp;
if(son[x])dfs2(son[x], tp);
for(int v : g[x])if(v != fa[x] && v != son[x])dfs2(v, v);
}
int lca(int u, int v){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]])swap(u, v);
u = fa[top[u]];
}
return dep[u] < dep[v] ? u : v;
}
int Dis(int u, int v){return dep[u] + dep[v] - 2 * dep[lca(u, v)];}
struct data{
int u, v, dis; bool flag;
data(){}
data(int a, int b, int c, int d){u = a; v = b; dis = c; flag = d;}
friend data operator + (const data &x, const data &y){
if(x.flag)return y; if(y.flag)return x;
int d1 = Dis(x.u, y.u), d2 = Dis(x.u, y.v), d3 = Dis(x.v, y.u), d4 = Dis(x.v, y.v);
int mxd = max(max(x.dis, y.dis), max(max(d1, d2), max(d3, d4)));
if(mxd == x.dis)return x;
if(mxd == y.dis)return y;
if(mxd == d1)return data(x.u, y.u, d1, 0);
if(mxd == d2)return data(x.u, y.v, d2, 0);
if(mxd == d3)return data(x.v, y.u, d3, 0);
if(mxd == d4)return data(x.v, y.v, d4, 0);
assert(0);
}
void init(int x){u = v = x; dis = 0; flag = false;}
void rev(){if(flag)flag = false, dis = 0; else flag = true, dis = -1;}
};
struct seg{
data t[maxn << 2 | 1];
void build(int x, int l, int r){
if(l == r)return t[x].init(l);
int mid = (l + r) >> 1;
build(x << 1, l, mid);
build(x << 1 | 1, mid + 1, r);
t[x] = t[x << 1] + t[x << 1 | 1];
}
void modify(int x, int l, int r, int pos){
if(l == r)return t[x].rev();
int mid = (l + r) >> 1;
if(pos <= mid)modify(x << 1, l, mid, pos);
else modify(x << 1 | 1, mid + 1, r, pos);
t[x] = t[x << 1] + t[x << 1 | 1];
}
void query(){printf("%d\n",t[1].dis);}
}T;
char s[5];
int main(){
freopen("quarrel.in","r",stdin);
freopen("quarrel.out","w",stdout);
n = read(), q = read();
for(int i = 1; i < n; ++i){
int u = read(), v = read();
g[u].push_back(v);
g[v].push_back(u);
}
dfs1(1); dfs2(1, 1); T.build(1, 1, n);
for(int i = 1; i <= q; ++i){
scanf("%s",s);
if(s[0] == 'C')T.modify(1, 1, n, read());
else T.query();
}
return 0;
}
C. 选数问题 V2
赛时奇奇怪怪的过了。
去掉平方因子
质因子数量小于等于 \(2\) ,在质因子之间(不够两个算上 \(1\))连边
问题变成找无向图最小环
不难发现环的元素必然有 \(<= 1000\) 的,所以只从这些位置出发 \(BFS\)
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 1e6 + 66;
int n, a[maxn];
int prime[maxn], cnt, mi[maxn];
bool flag[maxn];
void init(int maxn){
mi[1] = 1;
for(int i = 2; i <= maxn; ++i){
if(!flag[i])prime[++cnt] = i, mi[i] = i;
for(int j = 1; j <= cnt && i * prime[j] <= maxn; ++j){
flag[i * prime[j]] = true;
mi[i * prime[j]] = prime[j];
if(i % prime[j] == 0)break;
}
}
}
int nsq(int x){
int res = 1;
while(x != 1){
int now = mi[x];
x /= mi[x];
if(mi[x] == now)x /= mi[x];
else res = res * now;
}
return res;
}
vector<int>g[maxn];
queue<int>q;
int dis[maxn], fa[maxn];
int sol(){
for(int i = 1; i <= n; ++i)if(a[i] == 1)return 1;
for(int i = 1; i <= n; ++i){
int x = mi[a[i]], y = mi[a[i] / mi[a[i]]];
g[x].push_back(y); g[y].push_back(x);
}
int ans = INT_MAX;
for(int i = 1; i <= 1000; ++i){
for(int j = 1; j <= 1000000; ++j)dis[j] = INT_MAX;
dis[i] = 0; q.push(i);
while(!q.empty()){
int x = q.front(); q.pop();
if(dis[x] >= ans)continue;
for(int v : g[x])if(v != fa[x]){
if(dis[v] == INT_MAX){
dis[v] = dis[x] + 1;
fa[v] = x;
q.push(v);
}else ans = min(ans, dis[v] + dis[x] + 1);
}
}
}
return ans == INT_MAX ? -1 : ans;
}
int main(){
freopen("choose.in","r",stdin);
freopen("choose.out","w",stdout);
init(1e6); n = read();
for(int i = 1; i <= n; ++i)a[i] = nsq(read());
printf("%d\n",sol());
return 0;
}
D. nnntxdy
发现概率跟剩余人数有关
\(f_{i, s}\) 表示一共扣掉 \(i\) 滴血,当前存活的为 \(s\)
转移考虑这一次能否干死一个,在前面任意选择
最后还需要算总方案
\(g_{i, s}\) 表示在 \(s\) 中一共干掉 \(i\) 滴血,谁都没干死的方案数
暴力能过
正解把后面的变成 \(meet in middle\) 即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 205, mx = (1 << 15) + 5, mod = 998244353;
int n, m, a[maxn], inv[maxn], c[maxn][maxn];
int qpow(int x, int y){
int ans = 1;
for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
return ans;
}
int f[maxn][mx], g[mx][maxn], cnt[mx], sum[mx], lg[mx];
void add(int &x, int y){x += y; if(x >= mod) x -= mod;}
int main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
n = read(), m = read(); int S = (1 << n) - 1;
for(int i = 0; i < n; ++i)a[i] = read();
for(int i = 0; i < m; ++i){
c[i][0] = 1;
for(int j = 1; j <= m; ++j)c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
for(int i = 1; i <= n; ++i)inv[i] = qpow(i, mod - 2);
for(int i = 2; i <= S; i += i)lg[i] = lg[i >> 1] + 1;
for(int i = 1; i <= S; ++i)cnt[i] = cnt[i >> 1] + (i & 1);
for(int i = 1; i <= S; ++i)sum[i] = sum[i - (i & -i)] + a[lg[i & -i]];
f[0][S] = 1;
for(int i = 0; i < m; ++i){
for(int s = 1; s <= S; ++s)if(f[i][s]){
f[i][s] = 1ll * f[i][s] * inv[cnt[s]] % mod;
int dt = i + 1 - sum[S ^ s];
for(int j = 0; j < n; ++j)if(((s >> j) & 1) && a[j] <= dt)
add(f[i + 1][s ^ (1 << j)], 1ll * f[i][s] * c[dt - 1][a[j] - 1] % mod);
if(dt <= sum[s] - cnt[s])add(f[i + 1][s], f[i][s]);
}
}
g[0][0] = 1;
int n1 = n >> 1;
for(int i = 1; i < (1 << n1); ++i){
int las = i ^ (i & -i), dt = lg[i & -i];
for(int j = 0; j < a[dt]; ++j)
for(int k = 0; k + j <= m; ++k)
add(g[i][j + k], 1ll * g[las][k] * c[j + k][k] % mod);
}
int n2 = n - n1;
for(int i = 1; i < (1 << n2); ++i){
int tmp = i << n1;
int las = tmp ^ (tmp & -tmp), dt = lg[tmp & -tmp];
for(int j = 0; j < a[dt]; ++j)
for(int k = 0; k + j <= m; ++k)
add(g[tmp][j + k], 1ll * g[las][k] * c[j + k][k] % mod);
}
int ans = 0;
for(int i = 0; i <= S; ++i)if(f[m][i]){
int tmp = 0, s1 = i & ((1 << n1) - 1), s2 = i ^ s1, res = m - sum[i ^ S];
for(int j = 0; j <= res; ++j)add(tmp, 1ll * g[s1][j] * g[s2][res - j] % mod * c[res][j] % mod);
add(ans, 1ll * f[m][i] * cnt[S ^ i] % mod * tmp % mod);
}
printf("%d\n",ans);
return 0;
}
2023 省选联测43
真的是信心赛啊
A. 树上的数
每个点只会被删一次,直接搞就行
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 5e6 + 55;
int n, m, a, b, head[maxn], tot, res;
bool vis[maxn];
struct edge{int to, net;}e[maxn];
void add(int u, int v){e[++tot] = {v, head[u]}; head[u] = tot;}
queue<int>q;
void del(int s){
if(vis[s])return;
q.push(s);
while(!q.empty()){
int x = q.front(); q.pop();
vis[x] = true; --res;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(!vis[v])q.push(v);
}
}
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
res = n = read(), m = read();
a = read(), b = read();
add(1, 2); int fa = 1;
for(int i = 3; i <= n; ++i){
fa = ((1ll * fa * a + b) ^ 19760817) % (i - 1) + 1;
add(fa, i);
}
int q = read(), x = read(), y = read(), ans = 0;
for(int i = 1; i <= m; ++i){
if(i > 1)q = (((1ll * q * x + y) ^ 19760817) ^ (i << 1)) % (n - 1) + 2;
del(q); ans ^= res;
}
printf("%d\n",ans);
return 0;
}
B. 时代的眼泪
考虑答案转换为每个点子树内权值小于他的有多少
可以用 \(BIT\) 维护
然后需要换根\(DP\)
多查询几个值就行
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 1e6 + 55;
int n, q, w[maxn], h[maxn], fa[maxn];
vector<int>g[maxn];
ll ans[maxn], res;
int rf[maxn], rin[maxn], rall[maxn];
struct BIT{
int t[maxn];
#define lowbit(x) (x & -x)
void add(int x){while(x <= n){++t[x]; x += lowbit(x);}}
int query(int x){int ans = 0; while(x){ans += t[x]; x -= lowbit(x);} return ans;}
}T;
void dfs(int x){
rin[x] -= T.query(w[x] - 1);
if(fa[x])rf[x] -= T.query(w[fa[x]] - 1);
T.add(w[x]);
for(int v : g[x])if(v != fa[x]){fa[v] = x; dfs(v);}
rin[x] += T.query(w[x] - 1);
if(fa[x])rf[x] += T.query(w[fa[x]] - 1);
}
void change(int x){
for(int v : g[x])if(v != fa[x]){
ans[v] = ans[x] - rf[v] + rall[v] - rin[v];
change(v);
}
}
int main(){
freopen("tears.in","r",stdin);
freopen("tears.out","w",stdout);
n = read(), q = read();
for(int i = 1; i <= n; ++i)w[i] = h[i] = read();
sort(h + 1, h + n + 1);
for(int i = 1; i <= n; ++i)w[i] = lower_bound(h + 1, h + n + 1, w[i]) - h;
for(int i = 1; i < n; ++i){
int u = read(), v = read();
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1);
for(int i = 1; i <= n; ++i)rall[i] = T.query(w[i] - 1);
for(int i = 1; i <= n; ++i)ans[1] += rin[i];
change(1);
for(int i = 1; i <= q; ++i)printf("%lld\n",ans[read()]);
return 0;
}
C. 传统艺能
考虑维护\(f_a f_b f_c\)
表示以 \(a / b / c\) 结尾的不同的子序列的数量
每个点的 \(dp\) 值是前面最后一个 \(a / b / c\) 的 \(dp\) 值的和 \(+1\)
就是强制选择当前点,在前面选择一个字母必然越靠后情况越多,于是找最后一个,然后加上只选择自己的一个
发现可以矩阵简单维护
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int mod = 998244353;
const int maxn = 1e5 + 55;
struct matrix{
int a[4][4];
matrix(){memset(a, 0, sizeof(a));}
friend matrix operator * (const matrix &x, const matrix &y){
matrix res;
for(int i = 0; i < 4; ++i)
for(int k = 0; k < 4; ++k)
for(int j = 0; j < 4; ++j)
res.a[i][j] = (res.a[i][j] + 1ll * x.a[i][k] * y.a[k][j]) % mod;
return res;
}
void get(int c){
for(int i = 0; i < 4; ++i)
for(int j = 0; j < 4; ++j)
a[i][j] = (j == c || i == j);
}
};
int n, m; char s[maxn];
struct seg{
matrix t[maxn << 2 | 1];
void build(int x, int l, int r){
if(l == r)return t[x].get(s[l] - 'A');
int mid = (l + r) >> 1;
build(x << 1, l, mid);
build(x << 1 | 1, mid + 1, r);
t[x] = t[x << 1] * t[x << 1 | 1];
}
void modify(int x, int l, int r, int pos, int c){
if(l == r)return t[x].get(c);
int mid = (l + r) >> 1;
if(pos <= mid)modify(x << 1, l, mid, pos, c);
else modify(x << 1 | 1, mid + 1, r, pos, c);
t[x] = t[x << 1] * t[x << 1 | 1];
}
matrix 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)return query(x << 1, l, mid, L, R) * query(x << 1 | 1, mid + 1, r, L, R);
if(L <= mid)return query(x << 1, l, mid, L, R);
else return query(x << 1 | 1, mid + 1, r, L, R);
}
}T;
int query(int l, int r){
matrix res; res.a[0][3] = 1;
res = res * T.query(1, 1, n, l, r);
int ans = 0; for(int i = 0; i < 3; ++i)ans = (ans + res.a[0][i]) % mod;
return ans;
}
int main(){
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
n = read(), m = read();
scanf("%s",s + 1);
T.build(1, 1, n);
for(int i = 1; i <= m; ++i){
int op = read(), l = read();
if(op & 1){
scanf("%s",s + 1);
T.modify(1, 1, n, l, s[1] - 'A');
}else{
int r = read();
printf("%d\n",query(l, r));
}
}
return 0;
}
D. 铺设道路
首先贪心的选择能选择的最长区间,可以得到天数和最大值
这个可以直接笛卡尔树解决
考虑最小值,通过手模样例大眼观察胡乱猜想,感觉好像作为左右端点的位置和次数都不会变
那么计算出每个点作为左右端点出现的次数,然后从前向后贪心配对即可
事实上找端点可以通过差分解决
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 3e5 + 55, mod = 1e9 + 7;
int n, a[maxn];
int si[maxn], ls[maxn], rs[maxn], l[maxn], r[maxn];
int cl[maxn], cr[maxn];
int st[maxn], top, root;
int mi, mx;
ll day;
int sq(int x){return 1ll * x * x % mod;}
void dfs(int x, int val){
si[x] = 1;
if(ls[x])dfs(ls[x], a[x]), l[x] = l[ls[x]];
if(rs[x])dfs(rs[x], a[x]), r[x] = r[rs[x]];
cl[l[x]] += a[x] - val;
cr[r[x]] += a[x] - val;
day += a[x] - val;
mx = (mx + 1ll * (a[x] - val) * sq(r[x] - l[x] + 1) )% mod;
}
int main(){
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
n = read(); for(int i = 1; i <= n; ++i)a[i] = read();
for(int i = 1; i <= n; ++i)l[i] = r[i] = i;
for(int i = 1; i <= n; ++i){
int tmp = top;
while(tmp && a[st[tmp]] > a[i])--tmp;
if(tmp)rs[st[tmp]] = i;
if(tmp != top)ls[i] = st[tmp + 1];
st[top = ++tmp] = i;
}
root = st[1]; dfs(root, 0);
for(int l = 1, r = 1; l <= n; ++l){
while(cl[l]){
while(cr[r] == 0)++r;
int dt = min(cl[l], cr[r]);
mi = (mi + 1ll * sq(r - l + 1) * dt) % mod;
cl[l] -= dt;
cr[r] -= dt;
}
}
printf("%lld\n",day);
printf("%d\n",mx);
printf("%d\n",mi);
return 0;
}