多校A层冲刺NOIP2024模拟赛25

他怎么把贴吧封了啊啊啊啊……

签到题,bitset水过就行了。

点此查看代码
#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 = stdin,*OutFile = stdout;
    FILE *InFile = freopen("a.in","r",stdin),*OutFile = freopen("a.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,m;char s[N];
bitset<N> e[N];
inline void solve(){
    cin>>n>>m;
    rep(test,1,m,1){
        cin>>(s+1);
        bitset<N> S,T;
        rep(i,1,n,1){
            if(s[i] == '1' || s[i] == '3') T.set(i);
            if(s[i] == '2' || s[i] == '3') S.set(i);
        }
        rep(i,1,n,1){
            if(s[i] == '1') e[i] ^= S;
            if(s[i] == '2') e[i] ^= T;
            if(s[i] == '3') e[i] ^= S|T;
        }
    }
    ll ans = 0;
    rep(i,1,n,1){
        ans += e[i].count();
        if(e[i][i]) ans--;
    }
    cout<<ans/2;
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    solve();
}

序列

所有正解都因为没有特判or精度炸掉了,只有我一个过的,乐。讲一个可能与正解不同的做法(因为赛后看那么多正解被疯狂hack不止,所以没看,如果一样的话请告诉我这个唐氏),没有什么特判与炸int/long long的问题,但常数稍大。

操作的贪心策略和只有一次赋值操作比较显然,就不说了。

\(a_i\)表示进行赋值操作前/后时第\(i\)位数,\(ad_i\)为第\(i\)位加的数(jiashu),\(mul_i\)为第\(i\)位乘的数,\(M\)表示所有数的乘积,显然有\(M=\prod\limits_{i=1}^n(a_i+ad_i)\times mul_i\)

考虑每种操作带来的贡献。

  1. 赋值操作:如果将\(a_i\leftarrow y\),赋值前\(M_1=\frac{M}{(a_i+ad_i)\times mul_i}\times (a_i+ad_i)\times mul_i\),赋值后\(M_2=\frac{M}{(a_i+ad_i)\times mul_i}\times (y+ad_i)\times mul_i\),有\(M_2-M_1=M\times\frac{y-a_i}{a_i+ad_i}\)
  2. 加操作:等价于将\(ad_i\leftarrow ad_i+y\),加之前\(M_1=\frac{M}{(a_i+ad_i)\times mul_i}\times (a_i+ad_i)\times mul_i\),加之后\(M_2=\frac{M}{(a_i+ad_i)\times mul_i}\times (a_i+ad_i+y)\times mul_i\),有\(M_2-M_1=M\times\frac{y}{a_i+ad_i}\)
  3. 乘操作,等价于将\(mul_i\leftarrow mul_i\times y\),乘之前\(M_1=\frac{M}{(a_i+ad_i)\times mul_i}\times (a_i+ad_i)\times mul_i\),乘之后\(M_2=\frac{M}{(a_i+ad_i)\times mul_i}\times (a_i+ad_i)\times mul_i\times y\),有\(M_2-M_1=M\times(y-1)\)

发现\(M\)是无用的,不用管就行,只需要对于每一个\(i\),将其最大的\(y-1,\frac{y}{a_i+ad_i},\frac{y-a_i}{a_i+ad_i}\)加到set里,然后每次操作后,因为\(a_i\)\(ad_i\)都可能会更改,将已经插入的删除再插入新的即可,double可能会有精度问题,因为可能分子很小分母很大,所以用一个分数类即可。

时间复杂度\(O(n\log n)\),常数较大。

点此查看代码
#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 = stdin,*OutFile = stdout;
    FILE *InFile = freopen("b.in","r",stdin),*OutFile = freopen("b.out","w",stdout);
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
#define pii pair<db,int>
constexpr int N = 1e5 + 10,mod = 1e9 + 7;
int n,m,a[N];ll ad[N],mul[N],fz[N];
struct Segment_Tree{
    struct segment_tree{
        int l,r,val;
        #define l(x) tree[x].l
        #define r(x) tree[x].r
        #define val(x) tree[x].val
    }tree[N<<2];
    inline void pushup(int k){val(k) = 1ll*val(k<<1)*val(k<<1|1)%mod;}
    void build(int k,int l,int r){
        l(k) = l,r(k) = r;
        if(l == r) return val(k) = a[l],void();
        int mid = (l + r) >> 1;
        build(k<<1,l,mid);build(k<<1|1,mid+1,r);
        pushup(k);
    }
    void upd(int k,int pos,int val){
        if(l(k) == r(k)) return val(k) = val,void();
        int mid = (l(k) + r(k)) >> 1;
        if(pos <= mid) upd(k<<1,pos,val);else upd(k<<1|1,pos,val);
        pushup(k);
    }
    inline int qry(){return val(1);}
    #undef val
    #undef l
    #undef r
}T;
priority_queue<int> op[N][4];
struct frac{
    ll z,m;frac(){}
    frac(ll Z,ll M){ll gcd = __gcd(Z,M);z = Z/gcd;m = M/gcd;}
    friend bool operator <= (const frac &x,const int &y){return x.z <= y*x.m;}
    bool operator == (const frac &x)const{return z == x.z && m == x.m;}
    bool operator < (const frac &x) const{return z*x.m < x.z*m;}
};
struct node{
    frac del;int p,flag;
    node(frac D,int P,int F):del(D),p(P),flag(F){}
    bool operator < (const node &x) const {if(del == x.del)if(p == x.p) return flag < x.flag;else return p < x.p;else return del < x.del;}
};
set<node> q;
inline void solve(){
    cin>>n>>m;rep(i,1,n,1) cin>>a[i],fz[i] = a[i],mul[i] = 1,ad[i] = 0;T.build(1,1,n);
    auto ins = [&](int opr,int x,int y){
        if(opr == 1 && y <= a[x]) return;
        if(opr == 3 && y == 1) return;
        switch(opr){
            case 1:if(op[x][1].empty()) op[x][1].emplace(y);else if(op[x][1].top() < y) op[x][1].pop(),op[x][1].emplace(y);break;
            case 2:op[x][2].emplace(y);break;
            case 3:op[x][3].emplace(y);break;
        }
    };
    auto get = [&](int p){return (fz[p]+ad[p]%mod)*mul[p]%mod;};
    auto Ins = [](int p){
        if(op[p][1].size()) q.emplace(frac((op[p][1].top()-fz[p]),(fz[p]+ad[p])),p,1);
        if(op[p][2].size()) q.emplace(frac(op[p][2].top(),(fz[p]+ad[p])),p,2).second;
        if(op[p][3].size()) q.emplace(frac(op[p][3].top()-1,1),p,3);
    };
    rep(i,1,m,1){int op,x,y;cin>>op>>x>>y;ins(op,x,y);}
    rep(i,1,n,1) Ins(i); cout<<T.qry()<<' ';
    int now = m+1;
    rep(test,1,m,1){
        if(q.empty() || (*q.rbegin()).del < frac(0,1)){now = test;break;}
        auto p = (*q.rbegin()).p;
        auto d = (*q.rbegin()).del;
        auto flag = (*q.rbegin()).flag;q.erase(--q.end());
        if(flag == 1){
            auto x = op[p][1].top();op[p][1].pop();
            if(op[p][2].size()) q.erase(node(frac(op[p][2].top(),(fz[p]+ad[p])),p,2));
            if(op[p][3].size()) q.erase(node(frac(op[p][3].top()-1,1),p,3));
            fz[p] = x;T.upd(1,p,get(p));
        }
        else if(flag == 2){
            auto x = op[p][2].top();op[p][2].pop();
            if(op[p][1].size()) q.erase(node(frac((op[p][1].top()-fz[p]),(fz[p]+ad[p])),p,1));
            if(op[p][3].size()) q.erase(node(frac(op[p][3].top()-1,1),p,3));
            ad[p] += x;T.upd(1,p,get(p));
        }
        else{
            auto x = op[p][3].top();op[p][3].pop();            
            if(op[p][1].size()) q.erase(node(frac((op[p][1].top()-fz[p]),(fz[p]+ad[p])),p,1));
            if(op[p][2].size()) q.erase(node(frac(op[p][2].top(),(fz[p]+ad[p])),p,2));
            mul[p] = mul[p]*x%mod;T.upd(1,p,get(p));
        }
        Ins(p);cout<<T.qry()<<' ';
    }
    rep(i,now,m,1) cout<<T.qry()<<' ';
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    solve();
}

改不完啊……

image

字符串

发现将答案就是将\(s[l:r]\)中的字符按照\(t\)中的位置编号,求相邻逆序对数\(+1\)

考虑如何求逆序对数,发现值域很小,考虑对值域做点什么。

\(f_{k,i,j}\)表示线段树上\(k\)区间内以\(i,j\)为断点的个数,有\(f_{k,i,j}=f_{ls(k),i,j}+f_{rs(k),i,j}\)\(f_{k,r(ls(k)),l(rs(k))}+=1\)

然后直接放在线段树上跑就行了,时间复杂度\(O(mk^2\log n)\)

点此查看代码
#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 = stdin,*OutFile = stdout;
    FILE *InFile = freopen("d.in","r",stdin),*OutFile = freopen("d.out","w",stdout);
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 2e5 + 10;
int n,m,k,a[N],b[N];char s[N],t[N];
struct Segment_Tree{
    struct node{
        array<array<int,10>,10> v;
        int l,r;
        node(){memset(v.data(),0,sizeof(v));}
        inline void operator += (int val){
            array<array<int,10>,10> res;
            rep(i,0,k-1,1) rep(j,0,k-1,1) res[(i+val)%k][(j+val)%k] = v[i][j];
            v = move(res);l = (l + val)%k,r = (r + val)%k;
        }
        inline friend node operator + (const node &x,const node &y){
            node res;res.l = x.l,res.r = y.r;
            rep(i,0,k-1,1) rep(j,0,k-1,1) res.v[i][j] = x.v[i][j] + y.v[i][j];
            res.v[x.r][y.l]++;
            return res;
        }
    };
    struct segment_tree{
        int l,r,lz;node val;
        #define l(x) tree[x].l
        #define r(x) tree[x].r
        #define lz(x) tree[x].lz
        #define val(x) tree[x].val
    }tree[N<<2];
    inline void pushup(int k){val(k) = val(k<<1) + val(k<<1|1);}
    inline void pushdown(int p){
        if(!lz(p)) return;
        int ls = p<<1,rs = p<<1|1;
        val(ls) += lz(p);val(rs) += lz(p);
        lz(ls) += lz(p);lz(rs) += lz(p);lz(p) = 0;
        lz(ls) %= k,lz(rs) %= k;
    }
    void build(int k,int l,int r){
        l(k) = l,r(k) = r,lz(k) = 0;
        if(l == r) return val(k).l = val(k).r = a[l],void();
        int mid = (l + r) >> 1;
        build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k);
    }
    void upd(int p,int l,int r,int val){
        if(l <= l(p) && r(p) <= r) return val(p) += val,lz(p) += val,lz(p) %= k,void();
        int mid = (l(p) + r(p)) >> 1;pushdown(p);
        if(l <= mid) upd(p<<1,l,r,val);
        if(r > mid) upd(p<<1|1,l,r,val);
        pushup(p);
    }
    node qry(int k,int l,int r){
        if(l <= l(k) && r(k) <= r) return val(k);
        int mid = (l(k) + r(k)) >> 1;pushdown(k);
        if(r <= mid) return qry(k<<1,l,r);
        if(l > mid) return qry(k<<1|1,l,r);
        return qry(k<<1,l,r)+qry(k<<1|1,l,r);
    }
}T;
inline void solve(){
    auto change = [&](int *a,char *s,int len){rep(i,0,len,1) a[i] = s[i] - 'a';};
    cin>>n>>m>>k>>(s+1);change(a+1,s+1,strlen(s+1));T.build(1,1,n);
    rep(test,1,m,1){
        int op,l,r,x;cin>>op>>l>>r;
        if(op == 1) cin>>x,T.upd(1,l,r,x);
        else{
            cin>>t;auto res = T.qry(1,l,r).v;
            change(b,t,k-1);
            int ans = 0;
            rep(i,0,k-1,1) rep(j,0,i,1) ans += res[b[i]][b[j]];
            cout<<ans+1<<'\n';
        }
    }
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    solve();
}
p

挂个万穗爷。我也想被万穗爷抱着睡

image

posted @ 2024-11-21 20:43  CuFeO4  阅读(27)  评论(1编辑  收藏  举报