多校A层冲刺NOIP2024模拟赛19

讲个笑话:

(讨论时间)

huge:(叹气)这讨论啊,就是改不了,这换了铃声了,也没……

众人:现在是讨论时间啊。

huge:(停顿)那刚才大课间那会哇啦哇啦的……

图书管理

简要题意

给定一个长度为\(n(n\le 10^4)\)的排列,求\(\sum\limits_{l=1}^n\sum\limits_{r=l}^n[r-l为偶数]l\times r\times f_{l,r}\)

solution

签到题,但我\(ccx\)被卡了。

部分分是对顶堆、主席树之类的带\(\log\)做法。

枚举中位数,假设当前枚举到\(i\),然后将大于它的数的记为1,小于它的数的记为-1,求前缀和\(s\),如果\(1\le l\le i\le r\le n\)能产生贡献时当且仅当\(s_r-s_{l-1}=0\)

直接做就完了,用数组求常数较小,可以直接过。

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// #include<sys/timeb.h>
using namespace __gnu_pbds;
using namespace std;
// struct timeb timer;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
    FILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
    // FILE *ErrFile = freopen("err.err","w",stderr);
#else
    FILE *InFile = freopen("book.in","r",stdin),*OutFile = freopen("book.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = double;using ldb = long double;
const int N = 1e4 + 10;
int n,a[N];
namespace IO{
    char buf[1<<23],*p1,*p2;
    #define gc() (p1==p2&&(p2=(p1=buf)+fread_unlocked(buf,1,1<<23,stdin),p1==p2)?EOF:*p1++)
    #define pc putchar_unlocked
    template<class T>
    inline void read(T &x){
        x = 0;
        char s = gc();
        for(;s < '0' || s > '9';s = gc());
        for(;'0' <= s && s <= '9';s = gc()) x = (x<<1)+(x<<3)+(s^48);
    }
    template<class T,class... Args>
    inline void read(T &x,Args&... argc){read(x);read(argc...);}
    template<class T>
    inline void write(T x){
        static int sta[20],top = 0;
        do{sta[++top] = x%10,x /= 10;}while(x);
        do{pc(sta[top--]+'0');}while(top);
    }
    inline void write(char x){pc(x);}
    template<class T,class... Args>
    inline void write(T x,Args... argc){write(x);write(argc...);}
}using namespace IO;
int tp[N<<2];
inline void solve(){
    ll ans = 0;
    read(n);rep(i,1,n,1) read(a[i]);
    rep(l,1,n,1){
        int tot1 = 0,tot2 = 0;
        queue<int> q;
        rep(r,l,n,1){
            tot1 += (a[r] > a[l]),tot2 += (a[r] < a[l]);
            tp[tot1-tot2+n] += r;
        }
        tot1 = tot2 = 0;
        drep(j,l,1,1){
            tot1 += a[j] > a[l],tot2 += a[j] < a[l];
            ans += 1ll*j*tp[tot2-tot1+n]*a[l];
        }
        tot1 = tot2 = 0;
        rep(r,l,n,1){
            tot1 += (a[r] > a[l]),tot2 += (a[r] < a[l]);
            tp[tot1-tot2+n] = 0;
        }
    }    
    write(ans,'\n');
}
signed main(){
    // cin.tie(nullptr)->sync_with_stdio(false);
    solve();
}

两棵树

简要题意

给你两棵大小为\(n(n\le 2\times10^5)\)树,一个节点有\(\frac{1}{2}\)的概率出现在一棵树中,有\(\frac{1}{2}\)的概率出现在另一棵树中,求两棵树连通块个数乘积的期望数。

solution

考虑到连通块数=剩余的点数-剩余的边数,则贡献\(X\times Y\)可以拆成4部分,为\(\text{点}\times\text{点}-\text{边}\times\text{点}-\text{点}\times\text{边}+\text{边}\times\text{边}\)

  1. \(\text{点}\times\text{点}\)的贡献。

    考虑在\(T\)中选择点\(x\)\(U\)中选择点\(y\)。若\(x = y\),则贡献为\(0\),反之,贡献为\(\frac{1}{4}\)。所以总贡献为\(\frac{\mathrm{C}_{n}^2}{4}\)

  2. \(\text{边}\times\text{点}\)的贡献。

    考虑\(T\)中留下边\((x,y)\)\(U\)中留下点\(k\),则当\(x=k\)\(y=k\)时贡献为0,反之贡献为\(\frac{1}{8}\)。所以总贡献为\(\frac{(n-1)\times (n-2)}{8}\)

  3. \(\text{边}\times\text{边}\)的贡献。

    考虑\(T\)中留下\((x,y)\)\(U\)中留下\((u,v)\),当\(x,y,u,v\)中存在两个相同时,贡献为0,反之,贡献为\(\frac{1}{16}\)

    考虑如何计算,可以枚举\((x,y)\),统计符合条件的\((u,v)\)个数,就是\(n-1-deg_{U}x-deg_{U}y+[U\text{中存在}(u,v)=(x,y)]\)

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
    FILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
#else
    FILE *InFile = freopen("tree.in","r",stdin),*OutFile = freopen("tree.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = double;using ldb = long double;
constexpr int N = 2e5 + 10,mod = 998244353;
struct node{int x,y;}a[N],b[N];
int n,d[N];
inline int power(int a,int b,int mod){
    int res = 1;
    for(;b;b >>= 1,a = 1ll*a*a%mod)
        if(b&1) res = 1ll*res*a%mod;
    return res;
}
inline int Inv(int x){return power(x,mod-2,mod);}
set<int> st[N];
inline void solve(){
    cin>>n;
    rep(i,1,n-1,1) cin>>a[i].x>>a[i].y;
    rep(i,1,n-1,1) 
        cin>>b[i].x>>b[i].y,
        st[b[i].x].insert(b[i].y),st[b[i].y].insert(b[i].x),
        d[b[i].x]++,d[b[i].y]++;
    int ans = 0;
    ans = (ans + 1ll*n*(n-1)%mod*Inv(4)%mod)%mod;
    ans = (ans - 1ll*(n-1)%mod*(n-2)%mod*Inv(4)%mod + mod) % mod;
    if(n >= 4) rep(i,1,n-1,1) 
        ans = (ans + 1ll*(n-1-d[a[i].x]-d[a[i].y] + st[a[i].x].count(a[i].y))*Inv(16)%mod)%mod;
    cout<<ans<<'\n';    
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    solve();
}

函数

简要题意

定义一个函数\(f(x)=(x\oplus a)-b\),其中\(a,b\)为给定的参数。

有一个长度为\(n(n\le 10^6)\)的序列\(x\),给出\(q(q\le 10^6)\)次询问,每次给定\(a,b\),问是否存在\(i\in [1,n)\),满足\(f(x_i)\times f(x_{i+1})\le 0\)

solution

简单题,可是赛时没想到用Trie。

考虑求出\(x_i\oplus a\)最大的位置\(maxpos\)和最小的位置\(minpos\),如果\(f(x_{maxpos})\times f(x_{minpos}) > 0\)显然无解。

反之,考虑二分,每次取中点\(mid\),易知\(f(x_{mid})\)肯定与\(f(x_{maxpos})\)\(f(x_{minpos})\)中的一个异号,所以\(f(x_{maxpos})\)\(f(x_{minpos})\)\(f(x_{mid})\)中必然有一对异号,正确性显然。

时间复杂度\(O(n\log V + q\times(\log n+\log V))\approx 2e8\)略微卡常。

点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCAL
    FILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
#else
    FILE *InFile = freopen("fun.in","r",stdin),*OutFile = freopen("fun.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = double;using ldb = long double;
namespace IO{
    char buf[1<<23],*p1,*p2;
    #define gc() (p1==p2&&(p2=(p1=buf)+fread_unlocked(buf,1,1<<23,stdin),p1==p2)?EOF:*p1++)
    #define pc putchar_unlocked
    template<class T>
    inline void read(T &x){
        x = 0;
        char s = gc();
        for(;s < '0' || s > '9';s = gc());
        for(;'0' <= s && s <= '9';s = gc()) x = (x<<1)+(x<<3)+(s^48);
    }
    template<class T,class... Args>
    inline void read(T &x,Args&... argc){read(x);read(argc...);}
    template<class T>
    inline void write(T x){
        static int sta[20],top = 0;
        do{sta[++top] = x%10,x /= 10;}while(x);
        do{pc(sta[top--]+'0');}while(top);
    }
    inline void write(char x){pc(x);}
    template<class T,class... Args>
    inline void write(T x,Args... argc){write(x);write(argc...);}
}using namespace IO;
const int N = 1e6 + 10,V = 1e9 + 10,LV = 29;
int n,q,x[N];
struct Trie{
    int tree[N*30][2],tot,en[N*30],ex[N*30];
    inline void insert(int x,int pos){
        int p = 0;
        drep(i,LV,0,1){
            bool k = (x>>i)&1;
            if(!tree[p][k]) tree[p][k] = ++tot;
            p = tree[p][k];
        }
        if(!en[p]) en[p] = pos;
        ex[p] = max(ex[p],pos);
    }
    inline int qryn(int x){
        int p = 0;
        drep(i,LV,0,1){
            bool k = (x>>i)&1;
            if(tree[p][k]) p = tree[p][k];
            else p = tree[p][!k];
        }
        return en[p];
    }
    inline int qryx(int x){
        int p = 0;
        drep(i,LV,0,1){
            bool k = (x>>i)&1;
            if(tree[p][!k]) p = tree[p][!k];
            else p = tree[p][k];
        }
        return ex[p];
    }
}trie;
inline bool cmp(int x,int y){return (x<=0&&y>=0)||(x>=0&&y<=0);}
inline void solve(){
    read(n,q);rep(i,1,n,1) read(x[i]);
    rep(i,1,n,1) trie.insert(x[i],i);
    while(q--){
        int a,b;read(a,b);
        int l = trie.qryn(a);int r = trie.qryx(a);
        if(l > r) swap(l,r);
        auto f = [&](int pos){return (x[pos]^a)-b;};
        if(!cmp(f(l),f(r))){puts("-1");continue;}
        int pos1 = l,pos2 = r,ans = 0;
        int f1 = f(pos1),f2 = f(pos2);
        bool flag = true;
        while(l <= r){
            int mid = (l + r) >> 1;
            if(cmp(f2,f(mid))) flag = false,ans = mid,l = mid + 1;
            else  flag = true,ans = mid,r = mid - 1;
        }
        if(flag) write(ans-1,'\n');
        else write(ans,'\n');
    }
}
signed main(){solve();}

编辑

简要题意

给定\(k(k\le 30)\)和两个字符串\(S,T(|S|,|T|\le 5\times 10^4)\)\(\forall i\in[0,k]\),求出\(T\)中多少个非空子串与\(S\)的编辑距离恰好为\(i\)

solution

严格弱化版:[BJOI2015] 隐身术

部分分直接暴力DP。正解好像很难的样子,不会。

30pts

枚举 \(T\) 的某一子串进行编辑距离求解的DP,具体状态为让 \(A\) 变成 \(B\),现在只考虑 \(A[1:i]\) 变成 \(B[1:j]\) 的编辑距离为 \(f[i][j]\),转移时考虑删除,添加,修改第 \(i+1\) 个位置即可,时间复杂度为 \(O(n^4)\)​。

100pts

枚举每个后缀,\(f_{i,j}\) 表示最大的 \(x\),使得 \(S[1:x]\)\(T[1:x+j]\) 可以在 \(i\) 次编辑内得到,显然若 \(x\) 可以,所有\(x_0 \leq x\)\(S[1:x_0]\)\(T[1:x_0+j]\) 都可以在 \(i\) 次编辑内得到。

考虑如何转移,先考虑做完第 \(j\) 次编辑操作后往外延申,可以延申的即为 \(S\) 的一个后缀和 \(T\) 的一个后缀的最长公共前缀,即\(f_{i,j} = f_{i,j} + \text{LCP}(S[f_{i,j + 1}:|S|],T [f_{i,j} + j + 1 . .: |T|])\),随后我们可以通过对\(f_{i+1,j-1},f_{i+1,j},f_{i+1,j+1}\) 三项进行转移,即考虑下一次的编辑的具体操作是删除添加还是修改。

每次要算的两个后缀的前缀下标不会差超过 \(k\),因此一共至多求 \(O(nk)\) 次 LCP,可以利用二分+ hash 的方式解决。

记录每个后缀中 \(f_{i,j}=|S|\) 的那些状态,即可计算出最终答案,时间复杂度为 \(O(nk^2+nk \log n)\)

P

image

posted @ 2024-11-07 18:59  CuFeO4  阅读(32)  评论(0编辑  收藏  举报