11.9总结
11.9 GZEZ NOIP2022模拟测试赛(五十七)
PROBLEM A : [精神焕发(huan)]
题面描述:
给你一颗树,每个点可以回血,经过一次回血\(w[i]\)(有正有负)
\(q\)次询问:给出\(x , s , t\) 问初始血量为\(x\)是否能从\(s\)到达\(t\)
Solution:
先考虑一个特殊情况为若\(s\)到\(t\)时不是走最短路径而是走到一个无限回血点,那么必然经过一条两端权值和为正数的。
- 1.从\(s\)走到无限回血点再走到\(t\),我们需要统计\(s\)到\(t\)上的前缀最小值,可以发现前缀最小值是是个路径的回血或者是整个路径除去t点,否则\(s\)到\(t\)上一定有无限回血点。用倍增维护即可
- 2.从\(s\)直接走到\(t\)的最短路径通过 \(up\ and\ down\ DP\) 维护从每个点最少需要多少血才能到达一个无限血点。
Code:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
namespace fast_IO
{
#define FAST_IO
#define IOSIZE 100000
typedef long long ll;
typedef double db;
typedef long double ldb;
typedef __int128_t i128;
char ibuf[IOSIZE], obuf[IOSIZE];
char *p1 = ibuf, *p2 = ibuf, *p3 = obuf;
#ifdef ONLINE_JUDGE
#define getchar() ((p1==p2)and(p2=(p1=ibuf)+fread(ibuf,1,IOSIZE,stdin),p1==p2)?(EOF):(*p1++))
#define putchar(x) ((p3==obuf+IOSIZE)&&(fwrite(obuf,p3-obuf,1,stdout),p3=obuf),*p3++=x)
#endif//fread in OJ, stdio in local
#define isdigit(ch) (ch>47&&ch<58)
#define isspace(ch) (ch<33&&ch!=EOF)
struct fast_IO_t{
~fast_IO_t(){
fwrite(obuf, p3-obuf, 1, stdout);
}
bool flag = false;
operator bool() {
return flag;
}
}io;
template<typename T> inline T read() {
T s = 0; int w = 1; char ch;
while(ch=getchar(), !isdigit(ch)&&(ch!=EOF))
if(ch == '-') w = -1;
if(ch == EOF) return 0;
while(isdigit(ch))
s = s*10+ch-48, ch=getchar();
if(ch == '.') {
ll flt = 0; int cnt = 0;
while(ch=getchar(), isdigit(ch))
if(cnt < 18) flt=flt*10+ch-48, cnt++;
s += (db)flt/pow(10,cnt);
}
return s *= w;
}
template<typename T> inline bool read(T &s) {
s = 0; int w = 1; char ch;
while(ch=getchar(), !isdigit(ch)&&(ch!=EOF))
if(ch == '-') w = -1;
if(ch == EOF) return false;
while(isdigit(ch))
s = s*10+ch-48, ch=getchar();
if(ch == '.') {
ll flt = 0; int cnt = 0;
while(ch=getchar(), isdigit(ch))
if(cnt < 18) flt=flt*10+ch-48, cnt++;
s += (db)flt/pow(10,cnt);
}
return s *= w, true;
}
inline bool read(char &s) {
while(s = getchar(), isspace(s));
return s != EOF;
}
inline bool read(char *s) {
char ch;
while(ch=getchar(), isspace(ch));
if(ch == EOF) return false;
while(!isspace(ch))
*s++ = ch, ch=getchar();
*s = '\000';
return true;
}
template<typename T> void print(T x) {
static int t[20]; int top = 0;
if(x < 0) putchar('-'), x = -x;
do { t[++top] = x%10; x /= 10; } while(x);
while(top) putchar(t[top--]+48);
}
struct null_type{}; int pcs = 8;
null_type setpcs(int cnt)
{
pcs = cnt;
return null_type();
}
inline void print(null_type x){}
inline void print(double x) {
if(x < 0) putchar('-'), x = -x;
x += 5.0 / pow(10,pcs+1);
print((ll)(x)); x -= (ll)(x); putchar('.');
for(int i = 1; i <= pcs; i++)
x *= 10, putchar((int)x+'0'), x -= (int)x;
}
inline void print(float x) {
if(x < 0) putchar('-'), x = -x;
x += 5.0 / pow(10,pcs+1);
print((ll)(x)); x -= (ll)(x); putchar('.');
for(int i = 1; i <= pcs; i++)
x *= 10, putchar((int)x+'0'), x -= (int)x;
}
inline void print(long double x) {
if(x < 0) putchar('-'), x = -x;
x += 5.0 / pow(10,pcs+1);
print((i128)(x)); x -= (i128)(x); putchar('.');
for(int i = 1; i <= pcs; i++)
x *= 10, putchar((int)x+'0'), x -= (int)x;
}
inline void print(char x) {
putchar(x);
}
inline void print(char *x) {
for(int i = 0; x[i]; i++)
putchar(x[i]);
}
inline void print(const char *x) {
for(int i = 0; x[i]; i++)
putchar(x[i]);
}
#ifdef _GLIBCXX_STRING//string
inline bool read(std::string& s) {
s = ""; char ch;
while(ch=getchar(), isspace(ch));
if(ch == EOF) return false;
while(!isspace(ch))
s += ch, ch = getchar();
return true;
}
inline void print(std::string x) {
for(auto i = x.begin(); i != x.end(); i++)
putchar(*i);
}
inline bool getline(fast_IO_t &io, string s)
{
s = ""; char ch = getchar();
if(ch == EOF) return false;
while(ch != '\n' and ch != EOF)
s += ch, ch = getchar();
return true;
}
#endif
template<typename T, typename... T1>
inline int read(T& a, T1&... other) {
return read(a)+read(other...);
}
template<typename T, typename... T1>
inline void print(T a, T1... other) {
print(a); print(other...);
}
template<typename T>
fast_IO_t& operator >> (fast_IO_t &io, T &b){
return io.flag=read(b), io;
}
template<typename T>
fast_IO_t& operator << (fast_IO_t &io, T b) {
return print(b), io;
}
#define cout io
#define cin io
#define endl '\n'
}
using namespace fast_IO;
const int SIZE = 1e6+1000 ;
struct node{
int nxt ;
int to ;
}e[SIZE<<2] ;
ll w[SIZE] ;
int dep[SIZE],fa[SIZE][22];
int head[SIZE];
ll dis[SIZE] ;
ll smaller[SIZE][22],bigger[SIZE][22] ;
ll sum[SIZE] ;
int n , m , q ;
int tot ;
inline void add(int x , int y){
e[++tot] = (node) {head[x] , y} , head[x] = tot ;
e[++tot] = (node) {head[y] , x} , head[y] = tot ;
}
inline void dfs(int now , int f){
dep[now] = dep[f] + 1 , fa[now][0] = f ;
smaller[now][0] = bigger[now][0] = w[now] ;
dis[now] = w[now] + dis[f] ;
for(int i = 1 ; i <= 20 ; i += 1){
fa[now][i] = fa[fa[now][i-1]][i-1] ;
smaller[now][i] = min(smaller[now][i-1] , dis[now] - dis[fa[now][i-1]] + smaller[fa[now][i-1]][i-1]) ;
bigger[now][i] = max(bigger[now][i-1] , dis[now] - dis[fa[now][i-1]] + bigger[fa[now][i-1]][i-1]) ;
}
for(int i = head[now] ; i ; i = e[i].nxt){
int v = e[i].to ;if(v == f) continue ;
dfs(v , now) ;
}
}
inline int LCA(int x , int y){
if(dep[x] < dep[y]) swap(x , y) ;
for(int i = 20 ; i >= 0 ; i -= 1){
if(dep[fa[x][i]] >= dep[y]){
x = fa[x][i] ;
}
}
if(x == y) return x ;
for(int i = 20 ; i >= 0 ; i -= 1){
if(fa[x][i] != fa[y][i]){
x = fa[x][i] ;
y = fa[y][i] ;
}
}
return fa[x][0] ;
}
inline ll get(int x , int y){
int lca = LCA(x , y) ;
ll d = 0 , ans = 1e9 ;
for(int i = 20 ; i >= 0 ; i -= 1){
if(dep[fa[x][i]] >= dep[lca]){
ans = min(ans , d + smaller[x][i]) ;
d += dis[x] - dis[fa[x][i]] ;
x = fa[x][i] ;
}
}
ans = min(ans , w[lca] + d) ;
d += dis[y] - dis[lca] + w[lca] ;
ans = min(ans , d) ;
for(int i = 20 ; i >= 0 ; i -= 1){
if(dep[fa[y][i]] >= dep[lca]){
ans = min(ans , d - bigger[y][i]) ;
d -= dis[y] - dis[fa[y][i]] ;
y = fa[y][i] ;
}
}
return ans ;
}
inline void dfs1(int x , int fa){
if(w[x] + w[fa] > 0ll){
sum[x] = min(sum[x] , -w[x]) ;
sum[x] = max(sum[x] , 0ll) ;
sum[fa] = min(sum[fa] , -w[fa]) ;
sum[fa] = max(sum[fa] , 0ll) ;
}
for(int i = head[x] ; i ; i = e[i].nxt){
int v = e[i].to ; if(v == fa) continue ;
dfs1(v , x) ;
sum[x] = min(sum[x] , sum[v] - w[x]) ;
sum[x] = max(sum[x] , 0ll) ;
}
}
inline void dfs2(int x , int fa){
for(int i = head[x] ; i ; i = e[i].nxt){
int v = e[i].to ; if(v == fa) continue ;
sum[v] = min(sum[v] , sum[x] - w[v]) ;
sum[v] = max(sum[v] , 0ll) ;
dfs2(v , x) ;
}
}
signed main(){
int n , q ;
cin >> n >> q ;
for(int i = 1 ; i <= n ; i += 1) cin >> w[i] ;
for(int i = 1 ; i <= n - 1 ; i += 1){
int x , y ; cin >> x >> y ;
add(x , y) ;
}
dfs(1 , 0) ;
memset(sum , 0x3f , sizeof sum) ;
dfs1(1 , 0) , dfs2(1 , 0) ;
for(int i = 1 ; i <= q ; i += 1){
ll x ;
int y , z ; cin >> x >> y >> z ;
if(sum[y] <= x || -get(y , z) <= x){
puts("Yes") ;
}
else puts("No") ;
}
}
PROBLEM B : [顺逆序对(luan)]
题面描述:
对于一个 \(1,\dots,n\) 的排列 \(a_1,\dots,a_n,\)定义 \(i\) 处的顺序对数 \(f(i)\) 为满足 \(1\leq j<i\) 且 \(a_j<a_i\) 的 jj 的数量,定义$ i\(处的逆序对数\) g(i)$ 为满足 \(i<j\leq n\)且 \(a_j<a_i\)的 \(j\) 的数量。
给定 \(n\),对于每个$ k=0,1,\dots,n-1$,求出满足 \(\max_{i=1}^n|f(i)-g(i)|=k\)的 \(a_1,\dots,a_n\)的数量模 \(10^9+7\) 的值。
Solution:
先转化成对于每个\(k=0,1,\dots,n-1\)求\(max_{i-1}^{n}{\mid f\left(i\right) - g\left(i\right)\mid} \le k\)
考虑构造的过程是往序列里插数,在插第\(i+1\)个数时若放在第\(p\)和第\(p+1\)个数之间,\(f\left(i+1 \right) = p , g\left(i\right)=i-p\)现在只需要保证\(\mid p-\left(i-p\right)\mid \le k\)即可
设\(num\left(i\right)\)为\(\texttt{插入}i+1\)时满足条件的p的个数,显然可得:
\[c(i)=\left\{\begin{array}{ll} i+1 & i<k \\ k+[i \equiv k(\bmod 2)] & i \geq k \end{array}\right. \]然后转化成求\(\prod \limits_{i=0}^{n-1}num\left(i\right)=k! \prod \limits_{i=k}^{n-1}\left(k+\left[i\equiv k \left(mod\ 2\right)\right]\right)\)
Code:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const ll mod = 1e9+7 ;
ll n , m ;
inline ll ksm(ll a , ll b){
ll res = 1 ;
for( ; b ; b >>= 1 , a = a * a % mod){
if(b & 1) res = res * a % mod ;
}
return res ;
}
ll ans1 , ans2 ;
int main(){
cin >> n ;
ll fac = 1 ;
for (int k = 0; k < n; k++, fac = (fac * (k + !k) % mod))
{
ans1 = (fac * ksm(k, (n - k) >> 1) % mod * ksm(k + 1, (n - k + 1) >> 1) % mod) % mod ;
cout << (ans1 - ans2 + mod) % mod << ' ';
ans2 = ans1;
}
return 0 ;
}
PROBLEM C : [串串(chuan)]
题面描述:
现在有一个长度为 \(n\)的字符串 \(S\)与一个长度为 \(m\) 的字符串$ T$定义字符串 \(S_i\) 表示将从$ S$ 的第 \(i\)个字符后断开得到 \(A\),\(B\),将\(B\) 从尾到头形成的字符串设为 \(B'\),将 $B' $接在 \(A\)后面得到的字符串。对于每个 \(i\),\(\texttt{FAT}\)想知道 TT 在 \(S_i\)中出现的次数。
Solution:
\(T\)在 \(Si=AB\)中的出现次数是 \(T\) 在\(A\)中的出现次数 \(+T\texttt{在}B\) 中的出现次数 \(+ T \texttt{跨过} A \texttt{和} B\) 的出现次数 , 前两个很好处理,考虑最后一个\(T\) 的一个后缀要与 \(B\)的前缀匹配,剩余的前缀要与\(A\)的后缀匹配 , 可以先处理出所有跟 \(B\)的前缀匹配的\(T\)后缀,然后询问它们剩的前缀有多少个是\(A\)的后缀
可以建出 \(KMP\) 的 \(fail\ tree\) 解决
时间复杂度 \(O(nlogn)\)
Code:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6+10;
int n, m;
char s[N], t[N];
int nxt[N], len[N];
int ans[N], suf[N];
int ldfn[N], rdfn[N], cnt;
vector<int> ver[N];
void dfs(int x)
{
ldfn[x] = ++cnt;
for(auto y : ver[x]) dfs(y);
rdfn[x] = ++cnt;
}
void get_next(char *s, int *nxt);
namespace bit {
int a[N];
#define lowbit(i) (i&-i)
void add(int x, int v)
{ for(; x <= cnt; x += lowbit(x)) a[x] += v; }
int query(int x)
{ int ans = 0; for(; x >= 1; x -= lowbit(x)) ans += a[x]; return ans; }
}
int main()
{
cin >> (s+1) >> (t+1);
n = strlen(s+1);
m = strlen(t+1);
get_next(t, nxt);
for(int i = 1, j = 0; i <= n; i++)
{
while(j and t[j+1] != s[i]) j = nxt[j];
if(t[j+1] == s[i]) j++;
len[i] = j;
ans[i] = ans[i-1]+(j==m);
}
for(int i = 1; i <= m; i++)
ver[nxt[i]].push_back(i);
dfs(0);
reverse(t+1, t+m+1);
get_next(t, nxt);
int j = 0;
for(int i = 1; i <= n; i++)
{
while(j and t[j+1] != s[i]) j = nxt[j];
if(t[j+1] == s[i]) j++;
if(j == m) suf[i-m+1] = 1;
}
if(j == m) j = nxt[j];
vector<int> f;
for(; j; j = nxt[j])
f.push_back(j);
for(int i = n; i >= 1; i--)
{
while(!f.empty() and i+f.back() <= n)
{
bit::add(ldfn[m-f.back()], 1);
bit::add(rdfn[m-f.back()]+1, -1);
f.pop_back();
}
suf[i] += suf[i+1];
ans[i] += bit::query(ldfn[len[i]]);
}
for(int i = 1; i <= n; i++)
cout << ans[i]+suf[i+1] << "\n";
}
void get_next(char *s, int *nxt)
{
int n = strlen(s+1);
nxt[1] = 0;
for(int i = 2, j = 0; i <= n; i++)
{
while(j and s[j+1] != s[i]) j = nxt[j];
if(s[j+1] == s[i]) j++;
nxt[i] = j;
}
}
PROBLEM D : [烦死出题人的排列(fan)]
题面描述:
有 \(m\)个长度为$ n$排列 \(q_1,\dots,q_m\),现在你需要自己定义 \(k\)个置换,使得对于任意一个 \(q_i\),都可以从初始排列 \((1,2,\dots,n)\)以某种顺序应用这 \(k\) 个置换若干次得到。求 \(k\)的最小值是多少,并给出构造方案。
Solution:
什么东西🐂🐎🐂🐎🐂🐎🐂🐎🐂🐎🐂🐎🐂🐎🐂🐎
好吧我是🐂🐎🐂🐎🐂🐎🐂🐎🐂🐎🐂🐎🐂🐎🐂🐎
玄学东西我不到啊
Code:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 300, maxm = 500;
const long long MXV = 19000000000000000LL;
int n, m;
void print2() {
printf("2\n");
for (int i = 2; i <= n; i++) printf("%d ", i);
printf("1\n2 1 ");
for (int i = 3; i <= n; i++) printf("%d ", i);
}
struct permutation {
int a[maxn + 5];
permutation operator*(const permutation& b) const {
permutation c;
for (int i = 1; i <= n; i++) c.a[i] = b.a[a[i]];
return c;
}
} p[maxm + 5], unit;
long long a[maxm + 5], b[maxm + 5];
permutation qpow(const permutation& a, long long b) {
if (!b) return unit;
permutation t = qpow(a, b >> 1);
t = t * t;
if (b & 1) return t * a;
return t;
}
long long exgcd(long long a, long long b, long long& x, long long& y) {
if (b == 0) {
x = 1, y = 0;
return a;
}
long long d = exgcd(b, a % b, x, y), t = y;
y = x - (a / b) * y, x = t;
return d;
}
long long inv(long long r, long long m) {
long long x, y;
exgcd(r, m, x, y);
return (x % m + m) % m;
}
int id[maxn + 5], len[maxn + 5], bl[maxn + 5];
long long rem[maxn + 5], mod[maxn + 5];
mt19937_64 rng(time(0));
int main() {
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; i++) unit.a[i] = i;
bool z = true;
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++) {
scanf("%d", &p[i].a[j]);
if (p[i].a[j] != j) z = false;
}
if (z) return printf("0"), 0;
int tryc = 50;
while (tryc--) {
permutation q = unit;
for (int i = 1; i <= m; i++) {
long long e = rng() % INT_MAX;
q = q * qpow(p[i], e);
}
memset(bl, 0, sizeof(bl));
for (int i = 1, tc = 1; i <= n; i++, tc++) {
if (bl[i]) continue;
int x = i, c = 0;
while (!bl[x]) {
id[x] = c++, bl[x] = tc;
x = q.a[x];
}
len[tc] = c;
}
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (bl[j] != bl[p[i].a[j]]) goto ed;
mod[j] = len[bl[j]];
rem[j] = (id[p[i].a[j]] - id[j] + mod[j]) % mod[j];
}
for (int j = 2; j <= n; j++) {
long long m1 = mod[j - 1], m2 = mod[j], r1 = rem[j - 1], r2 = rem[j];
long long gcd = __gcd(m1, m2);
if ((r2 - r1) % gcd != 0) goto ed;
if ((long double)m1 / MXV * m2 / gcd > 1) { print2(); exit(0); }
mod[j] = m1 * m2 / gcd;
rem[j] = (((inv(m1 / gcd, m2 / gcd) * (r2 - r1) / gcd) % (m2 / gcd) * m1 + r1) % mod[j] + mod[j]) % mod[j];
}
}
printf("1\n");
for (int i = 1; i <= n; i++) printf("%d ", q.a[i]);
return 0;
ed:;
}
print2();
}