2025.5.9-2025.5.10做题记录

前言

感觉好忙好累,但不知道都干了什么。
怎么会有人盯着题看但是除了题什么都能想到。
甚至可以脑子里开一把 dwrg 都不去做题。

题目列表

A:Enchanted

Minecraft 背景好评。这种题没强在谁写在线谁……

读完题就能猜到要上个数据结构
操作 3 是单点修改,无需多言。
操作 1 的初看可能会觉得难以处理,但我们在看 \(q\) 的范围是 \(q\le 30\) 考虑直接状压附魔书等级。
然后合成其实就变成了加法,最大等级就是最高位 1 。
操作 2 影响的附魔书等级范围是 \(q\to k\) 取出这些位置的数,然后看末尾多少个连续 1 ,然后暴力计算贡献。(反正 \(q\le 30\)
然后关于操作 4,因为它没有强在,建立版本树直接 DFS 一遍,回溯时撤销操作就行。
总体而言可以拿树状数组维护,就不用写线段树了。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define lowbit(i) i&-i
using namespace std;
const int N=1e6+10;
const int mod=1e9+7;
int n,m,A,p,q;
int a[N],ans[N];
vector<int> vec[N];
struct node{
    int op,l,r,k;
}t[N];
int rnd(){
    A=(7*A+13)%19260817;
    return A;
}
int tr[N];
void update(int x,int k){
    for(int i=x;i<=n;i+=lowbit(i)){
        tr[i]+=k;
    }
}
int query(int x){
    int res=0;
    for(int i=x;i;i-=lowbit(i)){
        res+=tr[i];
    }
    return res;
}
void dfs(int x){
    int pre;
    if(t[x].op==0){
        for(int i=1;i<=n;i++){
            update(i,1<<(a[i]-1));
        }
    }else if(t[x].op==1){
        ans[x]=floor(log2(query(t[x].r)-query(t[x].l-1)))+1;
    }else if(t[x].op==2){
        int res=query(t[x].r)-query(t[x].l-1);
        res>>=(t[x].k-1);
        ans[x]=0;
        int k=1ll<<(t[x].k+1);
        while(res&1){
            ans[x]+=k;
            res>>=1;
            k<<=1;
            ans[x]%=mod;
        }
    }else if(t[x].op==3){
        int now=t[x].l,k=1ll<<(t[x].k-1);
        pre=a[now];
        update(now,k-(1<<(a[now]-1)));
        a[now]=t[x].k;
    }
    for(int v:vec[x]){
        dfs(v);
    }
    if(t[x].op==3){
        int now=t[x].l;
        update(now,(1<<(pre-1))-(1<<(a[now]-1)));
        a[now]=pre;
    }
}
signed main(){
    cin>>n>>m>>A>>p>>q;
    for(int i=1;i<=n;i++){
        a[i]=rnd()%q+1;
    }
    memset(ans,-1,sizeof(ans));
    for(int i=1;i<=m;i++){
        t[i].op=rnd()%p+1;
        if(t[i].op==1){
            int L=rnd()%n+1,R=rnd()%n+1;
            t[i].l=min(L,R);
            t[i].r=max(L,R);
        }else if(t[i].op==2){
            int L=rnd()%n+1,R=rnd()%n+1;
            t[i].l=min(L,R);
            t[i].r=max(L,R);
            t[i].k=rnd()%q+1;
        }else if(t[i].op==3){
            t[i].l=rnd()%n+1;
            t[i].r=t[i].l;
            t[i].k=rnd()%q+1;
        }else if(t[i].op==4){
            t[i].l=rnd()%i;
            t[i].r=t[i].l;
        }
    }
    for(int i=1;i<=m;i++){
        if(t[i].op==4){
            vec[t[i].l].push_back(i);
        }else{
            vec[i-1].push_back(i);
        }
    }
    dfs(0);
    for(int i=1;i<=m;i++){
        if(ans[i]!=-1) cout<<ans[i]<<'\n';
    }
    return 0;
}

B:Bi-ing Lottery Treekets

数据范围和题目基本明示我们是树形 dp 计数,考虑设计状态。不会!
按照往常树形 dp 的思路根本没有一个好用的状态设计,所以我们得换个思路。

定义 \(f_{i,j}\) 表示节点 \(i\) ,从外面接受 \(j\) 个球的方案数。
然后为了不重不漏的计算,我们定义这 \(j\) 个球完全相同,最后用 排列数 去统计方案数。
接下来我们考虑状态转移:
首先预处理出每个节点会落下多少球 \(a_i\),以及每个节点能容纳外面来的多少个球 \(b_i\),以及节点 \(i\) 的子树内有的球数 \(cnt_i\),和子树大小 \(siz_i\)
状态转移方程看代码吧

点击查看代码
#include<bits/stdc++.h>
#define ll long long

using namespace std;
const int N=4e3+10;
const int p=1e9+7;
int n,k;
//f[i][j] 表示节点 i 从外面接受 j 个球的方案数
ll f[N][N];
//a 表示节点内应有多少球,b 表示节点能容下多少球
int a[N],b[N],l[N],r[N];
ll fac[N],inv[N];
ll qpow(ll a,int b){
    ll ans=1;
    while(b){
        if(b&1) ans=ans*a%p;
        a=a*a%p;
        b>>=1;
    }
    return ans;
}
ll C(int a,int b){
    return fac[a]*inv[a-b]%p*inv[b]%p;
}
ll A(int a,int b){
    return fac[a]*inv[a-b]%p;
}
int siz[N],cnt[N];
void dfs(int u,bool flag){
    int ls=l[u],rs=r[u];
    if(l[u]) dfs(l[u],1);
    if(r[u]) dfs(r[u],0);
    siz[u]=siz[ls]+siz[rs]+1;
    cnt[u]=cnt[ls]+cnt[rs]+a[u];
    b[u]=siz[u]-cnt[u];
    //给这个节点多少个球
    for(int i=0;i<=b[u]&&i<=k-cnt[u];i++){
        int t1=flag ? ls : rs;
        int t2=flag ? rs : ls;
        //给第一个儿子节点多少个球
        for(int j=0;j<=b[t1]&&j<=a[u];j++){
            int y=min(i,b[t1]-j),F=(i==b[u]);
            ll tmp1=f[t1][j+y]*A(j+y,j)%p;
            ll tmp2=f[t2][a[u]+i-j-y-F]*A(a[u]+i-j-y,a[u]-j)%p;
            f[u][i]=(f[u][i]+1ll*C(a[u],j)*tmp1%p*tmp2%p)%p;
        }
    }
}
int main(){
#ifdef LOCAL
    freopen("D:/Desktop/cpp/data/code.in","r",stdin);
    freopen("D:/Desktop/cpp/data/code.out","w",stdout);
#endif
    cin>>n>>k;
    fac[0]=inv[0]=f[0][0]=1;
    for(int i=1;i<=n;i++){
        fac[i]=fac[i-1]*i%p;
        inv[i]=qpow(fac[i],p-2)%p;
    }
    for(int i=1;i<=k;i++){
        int x;cin>>x;
        a[x]++;
    }
    for(int i=1;i<=n;i++){
        cin>>l[i]>>r[i];
    }
    dfs(1,0);
    cout<<f[1][0];
    return 0;
}
posted @ 2025-05-10 11:56  Tighnari  阅读(17)  评论(2)    收藏  举报