暑假集训csp提高模拟27

赛时 rank 17,T1 100,T2 0,T3 0,T4 70

T2一眼OSU的拓展版,懒得打了。

T4写了一个奇怪的做法,轻轻松松拿70?

T3读假题了,然后间接导致了我与STL和pbds斗智斗勇。

题可能不算很难但是我糖

线性只因

用bitset记录每个数在二进制下的每一位,从高位到低位贪心即可。

如果可以的数小于m个,那么就舍弃这一位。

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
    FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
    // FILE *ErrFile=errfile("err.err");
#else
    FILE *Infile = stdin,*OutFile = stdout;
    //FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 1e6 + 10;
int n,m,a[N];
bitset<N> vis[31];
inline void solve(){
    cin>>n>>m;
    for(int i = 1;i <= n; ++i) cin>>a[i];
    for(int i = 30;i >= 0; --i){
        for(int j = 1;j <= n; ++j)
            if((a[j]>>i)&1) vis[i][j] = true;
    }
    bitset<N> res;res.set();
    int bg = 0;
    for(int i = 30;i >= 0; --i){
        if(vis[i].count() >= m){
            bg = i;
            break;
        }
    }
    res = vis[bg];
    int ans = 0;
    for(int i = bg;i >= 0; --i){
        if((vis[i]&res).count() >= m) ans |= (1<<i),res&=vis[i];
    }
    cout<<ans<<'\n';
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cout.tie(nullptr)->sync_with_stdio(false);
    solve(); 
}

金箱子

OSU拓展版,就是记录\(x^0,x^1,\dots,x^k\)的期望,递推转移

如何记录\(x^k\)的期望?由二项式定理,有\((x+a)^k=\sum_{i=0}^kC_k^ix^ia^{k-i}\)

然后就这么维护就可以了

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
    FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
    // FILE *ErrFile=errfile("err.err");
#else
    FILE *Infile = stdin,*OutFile = stdout;
    //FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 1e4 + 10,mod = 998244353;
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;
}
int f[N][110],t[N][110],fac[N],inv[N],n,k;
inline int C(int n,int m){return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;}
struct node{int p1,p2,a,b;}a[N];
inline void solve(){
    cin>>n>>k;
    for(int i = 1;i <= n; ++i) cin>>a[i].p1>>a[i].a>>a[i].b,a[i].p2 = 1-a[i].p1+mod;

    fac[0] = 1;
    for(int i = 1;i < N; ++i) fac[i] = 1ll * fac[i - 1] * i % mod;
    inv[N - 1] = power(fac[N - 1],mod - 2,mod);
    for(int i = N - 2;i >= 0; --i) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
    
    for(int i = 1;i <= n; ++i)
        for(int j = 0;j <= k; ++j)
            t[i][j] = (1ll*power(a[i].a,j,mod)*a[i].p1%mod+1ll*power(a[i].b,j,mod)*a[i].p2%mod)%mod;
    f[1][0] = 1;
    for(int i = 1;i <= k; ++i) f[1][i] = t[1][i];
    for(int i = 2;i <= n; ++i){
        f[i][0] = 1;
        for(int j = 1;j <= k; ++j){
            for(int k = 0;k <= j; ++k){
                f[i][j] = (f[i][j] + 1ll*C(j,k)*f[i-1][k]%mod*t[i][j-k]%mod)%mod;
            }
        }
    }
    cout<<f[n][k]<<'\n';
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cout.tie(nullptr)->sync_with_stdio(false);
    solve(); 
}

可持久化字符串

考虑到每次只是加减一个字符,那么将一个版本插到一颗\(Trie\)上,对于每次增减,直接在上一个版本的末尾处增减即可。

\(Trie\)树上每一个节点开一个vector,记录当前位置为那些版本的末尾。

将询问离线下来,记录一下。

\(Trie\)树上跑AC自动机,然后字符串匹配即可。
复杂度 \(O(n)\)

注意查询时要用拓扑排序,不然会T

赛后因为早版本的大样例有锅被硬控了2h

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
    FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
    FILE *ErrFile=errfile("err.err");
#else
    FILE *Infile = stdin,*OutFile = stdout;
    //FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
#define int long long
const int N = 1e5 + 10;
class BIT{
private:
    int tree[N];
#define lowbit(x) (x&(-x))
public:
    int mx = N-10;
    inline void update(int pos,int val){
        pos++;
        for(int i = pos;i <= mx + 3; i += lowbit(i)) tree[i] += val;
    }
    inline int query(int pos){
        int res = 0;
        for(int i = pos; i;i -= lowbit(i)) res += tree[i];
        return res;
    }
}T;
#define get(x) (x-'a'+1)
class Auto_Maton{
private:
    int trie[N][27],fail[N],fa[N],rd[N],cnt[N];
    vector<int> pos[N];
public:
    Auto_Maton(){pos[0].emplace_back(0);}
    int tot;
    inline int insert(int from,int id,char s){
        int x = get(s);
        int p = trie[from][x]?trie[from][x]:trie[from][x] = ++tot;
        fa[p] = from;
        pos[p].emplace_back(id);
        return p;
    }
    inline int erase(int from,int id){pos[fa[from]].emplace_back(id);return fa[from];}
    inline void build(){
        queue<int> q;
        for(int i = 1;i <= 26; ++i) 
            if(trie[0][i]) q.push(trie[0][i]),fail[trie[0][i]] = 0;
        while(q.size()){
            int x = q.front();q.pop();
            for(int i = 1;i <= 26; ++i){
                if(trie[x][i]) 
                    fail[trie[x][i]] = trie[fail[x]][i],q.push(trie[x][i]),++rd[trie[fail[x]][i]];
                else trie[x][i] = trie[fail[x]][i];
            }
        }
    }
    inline void get_ans(string s){
        build();int p = 0;
        for(char x:s) p = trie[p][get(x)],++cnt[p];
        queue<int> q;
        for(int i = 1;i <= tot; ++i) if(!rd[i]) q.push(i);
        while(q.size()){
            int x = q.front();q.pop();
            if(cnt[x]) for(auto k:pos[x]) T.update(k,cnt[x]);
            if(fail[x]){
                cnt[fail[x]] += cnt[x];
                --rd[fail[x]];
                if(!rd[fail[x]]) q.push(fail[x]);
            }
        }
        for(auto k : pos[0]) T.update(k,1);
    }
}AC;
int n,m,pos[N],cnt;
string b;
vector<pair<int,int> > que;
inline void solve(){
    cin>>n>>m>>b;
    char x;
    for(int i = 1,op,p,l,r;i <= n; ++i){
        cin>>op;
        if(op == 1){
            cin>>p>>x;
            ++cnt;
            pos[cnt] = AC.insert(pos[p],cnt,x);
        }
        if(op == 2){
            cin>>p;cnt++;
            pos[cnt] = AC.erase(pos[p],cnt);
        }
        if(op == 3){
            cin>>l>>r;
            que.emplace_back(make_pair(l,r));
        }
    }
    AC.get_ans(b);
    for(auto i:que){
        cout<<T.query(i.second+1)-T.query(i.first)<<'\n';
    }
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cout.tie(nullptr)->sync_with_stdio(false);
    solve(); 
}

圣诞树

写了一个神奇的暴力,就是用bitset记录每一个链的贡献,但是如果树退化成链就会寄,于是就将一条大链再砍成几个小链,暴力跳即可,时间复杂度未知,和\(O(n\sqrt{n})\)的莫队一样都是70pts(都在最后两个点被卡了)

正解是整体二分,不会,贺了

考虑利用每个朋友最多送给 chino 一件礼物这个性质,考虑离线后做整体二分,设当前二分的值为 \(mid\) ,那么我们需要对于每个询问求解树上路径中 \([L,mid]\) 值域中出现的数的种数。

发现所有权值的出现情况只有 \(3\) 种:当前权值只出现 \(1\) 次,当前权值出现 \(2\) 次并且出现的位置具有祖先关系,当前权值出现 \(2\) 次并且出现位置没有祖先关系。

假设所有询问 \((x,y)\) 均满足 \(x\)\(\operatorname{dfn}\) 序小于等于 \(y\)\(\operatorname{dfn}\) 序。

考虑第一种情况会贡献到的询问,设出现的位置为 \(u\) ,容易发现这会贡献到 \(\operatorname{dfn}_x\in [\operatorname{dfn}_u, \operatorname{dfn}_u + \operatorname{size}_u - 1], \operatorname{dfn}_y \in [\operatorname{dfn}_u + \operatorname{size}_u, n]\)\(\operatorname{dfn}_x \in [1, \operatorname{dfn}_u - 1], \operatorname{dfn}_y \in [\operatorname{dfn}_u, \operatorname{dfn}_u + \operatorname{size}_u - 1]\) 的询问,需要特殊处理 \(\operatorname{lca}(x, y) = u\) 的询问。

考虑第二种情况,首先按照第一种情况分别处理 \(u, v\) 两点,发现存在一些询问被贡献了两次,设 \(v\)\(u\) 方向上的儿子为 \(p\) ,那么 \(\operatorname{dfn}_x \in [1, \operatorname{dfn}_p - 1], \operatorname{dfn}_y \in [\operatorname{dfn}_u, \operatorname{dfn}_u + \operatorname{size}_u - 1]\)\(\operatorname{dfn}_x \in [\operatorname{dfn}_u, \operatorname{dfn}_u + \operatorname{size}_u - 1], \operatorname{dfn}_y \in [\operatorname{dfn}_p + \operatorname{size}_p, n]\) 的询问会被贡献两次。

考虑第三种情况,仍然按照第一种情况分别处理 \(u,v\) 两端,发现 \(\operatorname{dfn}_x \in [\operatorname{dfn}_u, \operatorname{dfn}_u + \operatorname{size}_u - 1], \operatorname{dfn}_y \in [\operatorname{dfn}_v, \operatorname{dfn}_v + \operatorname{size}_v - 1]\) 的询问会被贡献两次。

于是原问题转化为二维平面上做矩形加,单点查询,可以用扫描线解决,复杂度为 \(O(n\log^2n)\) ,使用树状数组后可以在 \(1200ms\) 通过本题。

posted @ 2024-08-22 17:52  CuFeO4  阅读(21)  评论(2编辑  收藏  举报