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();
}
posted @ 2022-11-09 19:06  Guier-Lime  阅读(31)  评论(1编辑  收藏  举报