CSP模拟<反思>(50~?)

csp模拟50

异或

疑惑是不是只有我是数位dp

考虑一个数 \(x\) 做出的贡献是这个数抑或上 \(x+1\) 也就是这个数二进制拆分下末尾连续1的长度加 1,所以直接数位dp,
\(len\) 表示长度,若这位为1\(len+1\) 否则变为 \(0\)

Code
#include<bits/stdc++.h>
#define int long long

using namespace std;
const int N=100;
int num[N];
int dp[N][N][2];
int solve(int pos,int len,int limit,int fk){
    if(!pos) return len+1;
    if(!limit && dp[pos][len][fk]!=-1) return dp[pos][len][fk];
    int up;
    if(limit) up=num[pos];
    else up=1;
    int ans=0;
    for(int i=0;i<=up;i++){
        int en;
        if(i==1) en=len+1;
        else en=0;
        if(fk==1){
            ans+=solve(pos-1,en,limit&(i==up),1);
            continue;
        }
        ans+=solve(pos-1,en,limit&(i==up),0);
    }
    if(!limit) dp[pos][len][fk]=ans;
    return ans;
}
signed main(){
    int n;
    scanf("%lld",&n);
    memset(dp,-1,sizeof(dp));
    n--;
    int cnt=0;
    while(n){
        num[++cnt]=n&1;
        n>>=1;
    }
    int ans=solve(cnt,0,1,1);
    cout<<ans<<endl;
}

首先需要明确的是,\(a\) 子树中所有与 \(a\) 的距离模 \(x\) 等于 \(y\) 的节点就是 \(a\) 子树中深度模 \(x\) 等于 \((dep_a+y)\mod x\)(下文设其为 \(k\))的节点。这样我们就可以把修改转化为将一个点的子树内所有深度模 \(x\)\(k\) 的节点权值加上 \(z\)

考虑暴跳,此时 \(x>\sqrt n\) ,然后进行分块,对于散块暴力,整块打标记,设 \(DS1_{dep,i}\) 表示深度为 \(dep\) 的编号为 \(i\) 的块的变化值,然后差分求,复杂度 修 \(O(1)\)\(O(\sqrt n)\)\(O(\sqrt n)\)

再考虑 \(x<\sqrt n\) ,此时暴跳会使复杂变成 \(O(n)\) ,所以根号分治,设 \(DS2_{x,k,i}\) 表示深度 \(\mod x\)\(k\) 的在第 \(i\) 块的变化,复杂度 \(\sqrt n\), 查 \(\sqrt n\)

查询的时候,对于散块直接暴力,整块分两种情况直接加就可以。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=3*1e5+10;
int head[N*2],ver[N*2],nex[N*2],tot=0;
int B1,B2;
int dep[N],id[N],cnt=0,size[N],d[N];
int pos[N],l[N],r[N],t;
int s[N],DS1[203][203][203],DS2[N][203];
int n,q;
void add(int x,int y){
    ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
}
void dfs(int x,int fa){
    dep[x]=dep[fa]+1;
    id[x]=++cnt;
    d[cnt]=dep[x];
    size[x]=1;
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==fa) continue;
        dfs(y,x);
        size[x]+=size[y];
    }
}
void change(int L,int R,int x,int k,int z,int y,int kk){
    int p=pos[L],q=pos[R];
    if(p==q){
        for(int i=L;i<=R;i++) if(d[i]%x==k) s[i]+=z;
    }
    else{
        for(int i=L;i<=r[p];i++) if(d[i]%x==k) s[i]+=z;
        for(int i=l[q];i<=R;i++) if(d[i]%x==k) s[i]+=z;
        if(x<=B2){
            for(int i=p+1;i<=q-1;i++){
                DS1[x][k][i]+=z;
            }
        }
        else{
            for(int i=kk;i<=n;i+=x){
                DS2[i][p+1]+=z;DS2[i][q]-=z;
            }
        }
    }
}
int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }
    dfs(1,0);
    B1=1500,B2=200;
    t=n/B1;
    for(int i=1;i<=n/B1;i++){
        l[i]=(i-1)*B1+1;
        r[i]=i*B1;
    }
    if(r[t]<n) t++,l[t]=r[t-1]+1,r[t]=n;
    for(int i=1;i<=t;i++){
        for(int j=l[i];j<=r[i];j++){
            pos[j]=i;
        }
    }
    for(int w=1;w<=q;w++){
        int op,x,y,v,z;
        scanf("%d",&op);
        if(op==1){
            scanf("%d%d%d%d",&v,&x,&y,&z);
            change(id[v],id[v]+size[v]-1,x,(dep[v]+y)%x,z,y,(dep[v]+y));
        }
        else{
            scanf("%d",&v);
            int ans=s[id[v]];
            for(int i=1;i<=B2;i++){
                ans+=DS1[i][dep[v]%i][pos[id[v]]];
            }
            for(int i=1;i<=pos[id[v]];i++){
                ans+=DS2[dep[v]][i];
            }
            printf("%d\n",ans);
        }
    }
}

2023NOIP A层联测8

集合

背包,求出每种数出现的次数,但是幂数很大,但是费马小定理,模数是质数, \(x^{p-1} \equiv 1 \mod p (p为质数)\) ,所以摸 \(p-1\)

点击查看代码
// ubsan: undefined
// accoders
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
inline __int128 read(){
    __int128 x(0),f(1);char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
    return x*f;
}

void print(__int128 x) {
  if(x<0)putchar('-'),x=-x;
  if(x>9)print(x/10);
  putchar(x%10+48);
}
unsigned long long f[205][20105];
int mgml(int x,unsigned long long p){
    int ans=1;
    while(p){
        if(p&1) ans=(ans*x)%mod;
        x=(x*x)%mod;
        p>>=1;
    }
    return ans;
}
signed main(){
    // freopen("collection.in","r",stdin);
    // freopen("collection.out","w",stdout);
    int n;
    scanf("%lld",&n);
    int sum=0;
    for(int i=1;i<=n;i++) sum=(sum+i)%mod;
    f[1][1]=1,f[1][0]=1;
    for(int i=2;i<=n;i++){
        for(int j=0;j<=sum;j++){
            f[i][j+i]=(f[i][j+i]+f[i-1][j])%(mod-1);
            f[i][j]=(f[i][j]+f[i-1][j])%(mod-1);
        }
    }
    int ans=1;
    for(int i=1;i<=sum;i++){
        ans=(ans*mgml(i,f[n][i])%mod)%mod;
    }
    printf("%lld",ans);

    
}

出租

摩尔定理,任意一段区间 \([l,r]\) 的租户满足人数的和小于 \(k*(r-l+1+d)\) ,将常数放到一边,得到 \(\sum_{l}^{r}{val_i-k}<k*d\) 所以可以求最长连续子段长度是否大于 \(k*d\) ,若大于则 \(YES\)

点击查看代码
// ubsan: undefined
// accoders
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=-1e9;
const int N=5*1e5+10;
int a[N];
int b[N];
struct seg{
    int l,r,sum,ma,sl,sr;
}tr[N<<2];
int n,m,k,d;
void pushdown(int p){
    tr[p].sum=tr[p*2].sum+tr[p*2+1].sum;
    tr[p].sl=max(tr[p*2].sl,tr[p*2].sum+tr[p*2+1].sl);
    tr[p].sr=max(tr[p*2+1].sr,tr[p*2+1].sum+tr[p*2].sr);
    tr[p].ma=max(tr[p*2].ma,max(tr[p*2+1].ma,tr[p*2].sr+tr[p*2+1].sl));
}
void build(int p,int l,int r){
    tr[p].l=l,tr[p].r=r;
    if(l==r){
        tr[p].sum=tr[p].ma=tr[p].sl=tr[p].sr=-k;
        return;
    }
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    pushdown(p);
}
void change(int p,int wh,int v){
    if(tr[p].l==tr[p].r){
        tr[p].sum+=v;
        tr[p].ma=tr[p].sl=tr[p].sr=tr[p].sum;
        return;
    }
    int mid=(tr[p].l+tr[p].r)/2;
    if(wh<=mid) change(p*2,wh,v);
    else change(p*2+1,wh,v);
    pushdown(p);
}
signed main(){
    freopen("lantern.in","r",stdin);
    freopen("lantern.out","w",stdout);
    scanf("%lld%lld%lld%lld",&n,&m,&k,&d);
    build(1,1,n);
    for(int p=1;p<=m;p++){
        int x,y;
        scanf("%lld%lld",&x,&y);
        change(1,x,y);
        if(tr[1].ma>k*d) printf("NO\n");
        else printf("YES\n");
    }
}

跳棋

设字符中的棋子个数为 \(i\) ,空白个数为 \(j\),那么存在的方案为 \(\binom{\lfloor i/2 \rfloor+j}{i}\)

考虑有不确定的,所以直接 dp。

\(dp_{i,j,k,0/1}\) 考虑到第 \(i\) 位,有 \(j\) 对1, \(k\) 个0,前面的1的个数为奇数/偶数。

然后第一维可以滚掉。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=505;
const int mod=1e9+7;
char s[N];
int f[2][N][N][3];
int C[N][N];
int n;
void init(){
    C[0][0]=1;
    for(int i=1;i<=n;i++){
        C[i][0]=1;
        for(int j=1;j<=i;j++){
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
        }
    }
}
signed main(){
    freopen("checkers.in", "r", stdin);
    freopen("checkers.out", "w", stdout);
    scanf("%lld",&n);
    scanf("%s",s+1);
    int op=0;
    f[0][0][0][0]=1;
    for(int i=0;i<n;i++){
        for(int j=0;j<=i;j++){
            for(int k=0;k<=i;k++){
                if(s[i+1]=='0' || s[i+1]=='?'){
                    f[op^1][j][k+1][0]=(f[op^1][j][k+1][0]+f[op][j][k][0]+f[op][j][k][1])%mod;
                }
                if(s[i+1]=='1' || s[i+1]=='?'){
                    f[op^1][j][k][1]=(f[op^1][j][k][1]+f[op][j][k][0])%mod;
                    f[op^1][j+1][k][0]=(f[op^1][j+1][k][0]+f[op][j][k][1])%mod;
                }
            }
        }
        for(int j=0;j<=i;j++)
            for(int k=0;k<=i;k++)
                f[op][j][k][0]=f[op][j][k][1]=0;
        op^=1;
    }
    init();
    int ans=0;
    for(int i=0;i<=n;i++){
        for(int j=0;j<=n;j++){
            if(i+j>n) break;
            int w=C[i+j][j]*(f[op][i][j][0]+f[op][i][j][1])%mod;
            ans=(ans+w)%mod;
        }
    }
    printf("%lld",ans);
}

连通块

树上dp,设 \(dp_{i,j}\) 表示以 \(i\) 为根的树中,限制为 \(j\) 的最大值,如果没限制则为 \(0\)
复杂度 \(O(n*k)\)

点击查看代码
// ubsan: undefined
// accoders
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
const int inf=-9187201950435737472;
vector<int> s[N];
int k;
int n,m;
int a[N];
int f[N][45];
bool flat[N];
int rk[N];
int mp[45][45];
int ans=0;
struct asd{
    int x,y;
}b[N];
void dfs(int x){
    if(flat[x]) f[x][rk[x]]=a[x];
    else f[x][0]=a[x];
    for(int i=0;i<s[x].size();i++){
        int y=s[x][i];
        dfs(y);
    }
    if(!s[x].size()) return;
    for(int i=0;i<s[x].size();i++){
        int y=s[x][i];
        if(!flat[y]){
            int mx=inf;
            for(int j=0;j<=k;j++) mx=max(mx,f[x][j]);
            if(mx==inf) continue;
            for(int j=0;j<=k;j++) f[x][j]=max(mx+f[y][j],f[x][j]);
        }
        else{
            int mx=inf;
            for(int j=0;j<=k;j++) if(!mp[rk[y]][j]) mx=max(mx,f[x][j]);
            if(mx==inf) continue;
            for(int j=0;j<=k;j++) f[x][j]=max(mx+f[y][j],f[x][j]);
        }
    }
    for(int i=0;i<=k;i++) ans=max(ans,f[x][i]);
}
signed main(){
    freopen("connection.in","r",stdin);
    freopen("connection.out","w",stdout);
    memset(f,128,sizeof(f));
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++){
        int x,y;
        scanf("%lld",&x);
        for(int j=1;j<=x;j++){
            scanf("%lld",&y);
            s[i].push_back(y);
        }
    }
    for(int i=1;i<=m;i++){
        scanf("%lld%lld",&b[i].x,&b[i].y);
        flat[b[i].x]=flat[b[i].y]=1;
    }
    k=0;
    for(int i=1;i<=n;i++){
        if(flat[i]) rk[i]=++k;
    }
    for(int i=1;i<=m;i++){
        int x=rk[b[i].x],y=rk[b[i].y];
        mp[x][y]=mp[y][x]=1;
    }
    dfs(1);
    printf("%lld",ans);
}

2023NOIP A层联测9

挂分记 T1(40)+T4(40)

紫罗兰

为啥我考场想矩阵,其实是一个 \(bfs\) ,以每个点为起点算出答案,需要除以环长,奇环还需要除以2,因为会从两个方向,还有就是需要记录到某个点的最短路个数,否则会算少,其实就是加入访问的点为当前点深度加一,则个数加加。

点击查看代码
// ubsan: undefined
// accoders
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=6002;
int n,m;
int head[N*2],ver[N*2],nex[N*2],tot=0;
int d[N];
void add(int x,int y){
    ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
}
int ans=(1<<20);
int sum[N];
queue<int> q;
int num[N];
void bfs(int x){
    q.push(x);
    memset(d,-1,sizeof(d));
    memset(num,0,sizeof(num));
    d[x]=0;
    num[x]=1;
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int i=head[x];i;i=nex[i]){
            int y=ver[i];
            if(d[y]!=-1 && d[y]<d[x]) continue;
            if(d[y]==-1){
                d[y]=d[x]+1;
                q.push(y);
            }
            else{
                sum[d[x]+d[y]+1]+=num[y];
                ans=min(ans,d[x]+d[y]+1);
            }
            if(d[y]==d[x]+1) num[y]++;
        }
    }
}
signed main(){
    // freopen("B.in","r",stdin);
    // freopen("B.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++){
        int x,y;
        scanf("%lld%lld",&x,&y);
        add(x,y),add(y,x);
    }
    for(int i=1;i<=n;i++){
        bfs(i);
    }
    int cnt=sum[ans];
    if(ans%2==0){
        cnt=cnt/ans;
    }
    else{
        cnt/=2;
        cnt/=ans;
    }
    printf("%lld",cnt);
}

风信子

超级钢琴+线段树(k=1)


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

using namespace std;
const int N=1e5+10;
int a[N];
struct seg{
    int l,r,mx,mn,x,y;
    int pmx,pmn;
    int lazy;
    int ans;
}tr[N*4];
struct asd{
    int l,r,ls,rs,x,y,ans;
    friend bool operator <(asd a,asd b){
        return a.ans<b.ans;
    }
};
priority_queue<asd> q;
void pushdown(int p){
    if(tr[p].lazy){
        tr[p*2].lazy+=tr[p].lazy;
        tr[p*2+1].lazy+=tr[p].lazy;
        tr[p*2].mx+=tr[p].lazy;
        tr[p*2].mn+=tr[p].lazy;
        tr[p*2+1].mx+=tr[p].lazy;
        tr[p*2+1].mn+=tr[p].lazy;
        tr[p].lazy=0;
    }
}
seg pushup(seg a,seg b){
    seg c;
    c.lazy=0;
    c.l=a.l,c.r=b.r;
    if(a.mx>b.mx) c.mx=a.mx,c.pmx=a.pmx;
    else c.mx=b.mx,c.pmx=b.pmx;
    if(a.mn<b.mn) c.mn=a.mn,c.pmn=a.pmn;
    else c.mn=b.mn,c.pmn=b.pmn;
    c.ans=a.ans;
    c.x=a.x,c.y=a.y;
    if(b.ans>c.ans){
        c.ans=b.ans;
        c.x=b.x,c.y=b.y;
    }
    if(a.mx-b.mn>c.ans){
        c.ans=a.mx-b.mn;
        c.x=a.pmx,c.y=b.pmn;
    }
    return c;
}
void build(int p,int l,int r){
    tr[p].l=l,tr[p].r=r;
    tr[p].lazy=0;
    if(l==r){
        tr[p].mx=tr[p].mn=a[l];
        tr[p].pmx=tr[p].pmn=l;
        tr[p].x=tr[p].y=l;
        tr[p].ans=0;
        return;
    }
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    tr[p]=pushup(tr[p*2],tr[p*2+1]);
}
void change(int p,int l,int r,int v){
    if(tr[p].l>=l && tr[p].r<=r){
        tr[p].lazy+=v;
        tr[p].mn+=v,tr[p].mx+=v;
        return;
    }
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)/2;
    if(l<=mid) change(p*2,l,r,v);
    if(r>mid) change(p*2+1,l,r,v);
    tr[p]=pushup(tr[p*2],tr[p*2+1]);
}
seg ask(int p,int l,int r){
    if(tr[p].l>=l && tr[p].r<=r){
        return tr[p];
    }
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)/2;
    seg ls,rs;
    ls.l=ls.r=0;
    rs.l=rs.r=0;
    if(l<=mid) ls=ask(p*2,l,r);
    if(r>mid) rs=ask(p*2+1,l,r);
    if(ls.l==0) return rs;
    else if(rs.l==0) return ls;
    else return pushup(ls,rs);
}
void split_A(int l,int r){ 
    seg qw=ask(1,l,r);
    asd a;
    a.l=a.ls=l,a.r=a.rs=r;
    a.ans=qw.ans;
    a.x=qw.x,a.y=qw.y;
    q.push(a);
}
void split_B(int l,int r,int ls,int rs){
    seg qw=ask(1,l,r);
    seg we=ask(1,ls,rs);
    asd a;
    a.l=l,a.r=r;
    a.ls=ls,a.rs=rs;
    a.ans=qw.mx-we.mn;
    a.x=qw.pmx,a.y=we.pmn;
    q.push(a);
}
int query(int L,int R,int k){
    int ans=0;
    while(!q.empty()) q.pop();
    split_A(L,R);
    while(k--){
        asd a=q.top();
        q.pop();
        ans+=a.ans;
        int l=a.l,r=a.r,x=a.x,y=a.y;
        int ls=a.ls,rs=a.rs;
        if(l==ls){
            if(x>l) split_A(l,x-1);
            if(x>l) split_B(l,x-1,x,r);
            if(x!=y) split_A(x,x);
            if(x<y-1) split_B(x,x,x+1,y-1);
            if(y<r) split_B(x,x,y+1,r);
            if(x<r) split_A(x+1,r);
        }
        else{
            if(x>l) split_B(l,x-1,ls,rs);
            if(ls<y) split_B(x,x,ls,y-1);
            if(y<rs) split_B(x,x,y+1,rs);
            if(x<r) split_B(x+1,r,ls,rs);
        }
    }
    return ans;
}
signed main(){
    freopen("D.in","r",stdin);
    freopen("D.out","w",stdout);
    int n,m;
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    build(1,1,n);
    for(int i=1;i<=m;i++){
        int op,l,r,x;
        scanf("%lld%lld%lld%lld",&op,&l,&r,&x);
        if(op==1) change(1,l,r,x);
        else{
            int ans=query(l,r,x);
            printf("%lld\n",ans);
        }
    }
}

CSP模拟57联测19 _2023NOIP A层联测12

直径的性质,所以在树上离一个点最远的点一定出于直径之一,所以倍增跳就可以。

\(dp\),设 \(dp_{i,j}\) 表示二进制第 \(j\) 位为 \(1\) 能到达 \(i\) 的最大的点。考虑转移,设 \(f_{i,j}\) 表示 \(i\) 之前二进制第 \(j\) 位为 \(1\) 最大点。然后

\(dp_{i,j}=max(f_{i,j})\) 此时 \(a_i\) 必须二进制第 \(j\) 位为 \(1\)

\(dp_{i,j}=max(dp_{f_{i,k},j})\) 此时 \(a_i\) 必须二进制第 \(k\) 位为 \(1\)

判断时,枚举 \(dp_{r,k}\) \(a_l \& (1<<k)=1\) 如果位置大于 \(x\) ,就可以。

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

using namespace std;
const int N=3*1e5+10;
int a[N];
int f[N][50];
int dp[N][50];
signed main(){
    freopen("and.in","r",stdin);
    freopen("and.out","w",stdout);
    int n,q;
    scanf("%lld%lld",&n,&q);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=20;j++){
            f[i][j]=f[i-1][j];
            if(a[i-1]&(1ll<<(j-1))){
                f[i][j]=max(f[i][j],i-1);
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=20;j++){
            if(a[i]&(1ll<<(j-1))) dp[i][j]=max(dp[i][j],f[i][j]);
            for(int k=1;k<=20;k++){
                if(a[i]&(1ll<<(k-1))){
                    dp[i][j]=max(dp[i][j],dp[f[i][k]][j]);
                }
            }
        }
    }
    for(int i=1;i<=q;i++){
        int x,y;
        scanf("%lld%lld",&x,&y);
        int getans=0;
        for(int j=1;j<=20;j++){
            if(a[x]&(1ll<<(j-1))){
                if(dp[y][j]>=x) getans=1;
            }
            if(getans) break;
        }
        if(getans) printf("Shi\n");
        else printf("Fou\n");
    }
}

小恐龙

对于每个塔有一个时间限制 \(lim\) 表示从 \(0\) 开始恢复到回满的最小时间。

如果要求一个区间内的点从一定时间从零开始回复的值,用主席树维护前缀和,相当于二维偏序,对于小于 \(t\)\(c\) 求和,对于大于等于 \(r\) 求和再乘上时间。对于一个怪,可以推平一段,所以维护一个栈,里面是一些线段和一些点,线段上可以二分,点直接判,每次询问会合并一些,也会增加一些点,但点是常数个。

点击查看代码
%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2*1e5+10;
struct tree{
    int l,r,cs,rs;
}tr[N*32],ans;
struct qwe{
    int cs,rs;
};
int cs[N],rs[N];
int rt[N],tot=0;
void build(int &p,int l,int r){
    p=++tot;
    tr[p].cs=tr[p].rs=0;
    if(l==r) return;
    int mid=(l+r)/2;
    build(tr[p].l,l,mid);
    build(tr[p].r,mid+1,r);
}
void change(int p,int &now,int wh,int v,int l,int r){
    now=++tot;
    tr[now]=tr[p];
    if(l==r){
        tr[now].cs+=cs[v];
        tr[now].rs+=rs[v];
        return;
    }
    int mid=(l+r)>>1;
    if(wh<=mid) change(tr[p].l,tr[now].l,wh,v,l,mid);
    else change(tr[p].r,tr[now].r,wh,v,mid+1,r);
    tr[now].cs=tr[tr[now].l].cs+tr[tr[now].r].cs;
    tr[now].rs=tr[tr[now].l].rs+tr[tr[now].r].rs;
    return;
}
void ask(int p1,int p2,int l,int r,int ls,int rs){
    if(ls>rs) return;
    if(ls<=l && r<=rs){
        ans.cs=ans.cs+tr[p2].cs-tr[p1].cs;
        ans.rs=ans.rs+tr[p2].rs-tr[p1].rs;
        return;
    }
    int mid=(l+r)>>1;
    if(ls<=mid) ask(tr[p1].l,tr[p2].l,l,mid,ls,rs);
    if(rs>mid) ask(tr[p1].r,tr[p2].r,mid+1,r,ls,rs);
    return;
}
struct zxc{
    int l,r,typ,t;
}st[N];
// tpy 1: 有值
// tpy 0: 空
struct asd{
    double ls;
    int id;
}a[N];
double uu[N];
bool amp(asd a,asd b){
    return a.ls<b.ls;
}
int rk[N];
int ms[N];
inline int read(){
	int x(0);bool f(0);char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar()) f^=ch=='-';
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
	return f?x=-x:x;
}
signed main(){
    freopen("dinosaurs.in","r",stdin);
    freopen("dinosaurs.out","w",stdout);
    int n;
    scanf("%lld",&n);
    int sum=0;
    for(int i=1;i<=n;i++){
        cs[i]=read(),rs[i]=read();
        ms[i]=cs[i];
        sum+=cs[i];
        a[i].ls=1.0*cs[i]/(1.0*rs[i]);
        a[i].id=i;
    }
    sort(a+1,a+n+1,amp);
    for(int i=1;i<=n;i++){
        uu[i]=a[i].ls;
        rk[a[i].id]=i;
    }
    for(int i=1;i<=n;i++) change(rt[i-1],rt[i],rk[i],i,0,n);
    int head=1,tail=0;
    for(int i=1;i<=n;++i){
        st[++tail]={i,i,1,0};
    }
    int cnt=0;
    int q;
    scanf("%lld",&q);
    while(q--){
        int T,h;
        T=read(),h=read();
        for(int i=head;i<=tail;++i){
            zxc s=st[i];
            int t=s.t,l=s.l,r=s.r;
            if(s.typ==1){
                ms[l]=min(cs[l],ms[l]+rs[r]*(T-t));
                if(ms[l]==h){
                    h=0;
                    st[i]={1,l,0,T};
                    head=i;
                    ms[l]=0;
                    break;
                }
                if(h>ms[l]) h-=ms[l];
                else{
                    ms[l]-=h;
                    h=0;
                    if(i==head) st[i]={l,r,1,T};
                    else{
                        st[i]={l,r,1,T};
                        st[i-1]={1,l-1,0,T};
                        head=i-1;
                    }
                    break;
                }
            }
            else{
                int ls=l,rs=r;
                int anss=0;
                int ts=lower_bound(uu+1,uu+n+1,T-t)-uu;
                int sum=0;
                ans.cs=ans.rs=0;ask(rt[l-1],rt[r],0,n,0,ts-1);
                sum=ans.cs+sum;
                ans.cs=ans.rs=0;ask(rt[l-1],rt[r],0,n,ts,n);
                sum=ans.rs*(T-t)+sum;
                if(h>sum){
                    h-=sum;
                    continue;
                }
                while(ls<=rs){
                    int mid=(ls+rs)/2;
                    int w=0;
                    ans.cs=ans.rs=0;ask(rt[l-1],rt[mid],0,n,0,ts-1);
                    w=ans.cs+w;
                    ans.cs=ans.rs=0;ask(rt[l-1],rt[mid],0,n,ts,n);
                    w=ans.rs*(T-t)+w;
                    if(w>=h){
                        anss=mid;
                        sum=w;
                        rs=mid-1;
                    }
                    else ls=mid+1;
                }
                int w=0;
                w=sum;
                w-=h;
                h=0;
                ms[anss]=w;
                if(anss==r){
                    st[i]={anss,anss,1,T};
                    st[i-1]={1,anss-1,0,T};
                    head=i-1;
                }
                else{
                    st[i]={anss+1,r,0,t};
                    st[i-1]={anss,anss,1,T};
                    st[i-2]={1,anss-1,0,T};
                    head=i-2;
                }
                break;
            }
        }
        if(h>0){
            cnt+=h;
            st[tail]={1,n,0,T};
            head=tail;
        }
    }
    // cerr<<" h ";
    printf("%lld",cnt);
}
/*
2012071806018
2012085820295
2103386905648
2103486301372
2095554685252
2109804643826
2110010532227
2162578051794
2162359988216
2162536990636
2162583801592
*/

CSP模拟57联测19 2023NOIP A层联测13

传话游戏

烂,哈希被卡 \(40 pts\)

全球覆盖

横纵可以分开,而且计算起来是一样的。

所以考虑横,对于关键点之间每一段线段,可以唯一对应全部点对的方案,然后求一个每种方案线段之和最大值 \(n^2\)

所以考虑排个序,从左到右,然后异或 \(hass\) 维护状态,一开始为外侧,扫到的起点改为内侧,扫到出点改为外侧。

\(mt19937\_ 64\) 就可以。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
const int mod=998244353;
struct asd{
    int x1,y1,x2,y2;
}a[N];
struct qwe{
    int x,id,op;
}b[N],c[N];
int n,X,Y;
int mgml(int x,int p){
    int ans=1;
    while(p){
        if(p&1) ans=(ans*x)%mod;
        x=(x*x)%mod;
        p>>=1;
    }
    return ans;
}
unordered_map<int,int>mp;
bool amp(qwe a,qwe b){
    return a.x<b.x;
}
mt19937_64 mt(clock());
int rnd[N];
signed main(){
    freopen("globe.in","r",stdin);
    freopen("globe.out","w",stdout);
    scanf("%lld%lld%lld",&n,&X,&Y);
    int cnt1=0,cnt2=0;
    for(int i=1;i<=n;i++){
        scanf("%lld%lld%lld%lld",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2);
        b[++cnt1]={min(a[i].x1,a[i].x2),i,1};b[++cnt1]={max(a[i].x1,a[i].x2),i,0};
        c[++cnt2]={min(a[i].y1,a[i].y2),i,1};c[++cnt2]={max(a[i].y1,a[i].y2),i,0};
    }
    for(int i=1;i<=n;i++) rnd[i]=mt();
    sort(b+1,b+cnt1+1,amp);
    sort(c+1,c+cnt2+1,amp);
    int mx=0;
    int zt=0;
    for(int i=1;i<=cnt1;i++){
        mp[zt]+=(b[i].x-b[i-1].x);
        mx=max(mx,mp[zt]);
        zt=zt^rnd[b[i].id];
    }
    mp[zt]+=(X-b[cnt1].x);
    mx=max(mx,mp[zt]);
    int my=0;
    zt=0;
    mp.clear();
    for(int i=1;i<=cnt2;i++){
        mp[zt]+=(c[i].x-c[i-1].x);
        my=max(my,mp[zt]);
        zt=zt^rnd[c[i].id];
    }
    mp[zt]+=(Y-c[cnt2].x);
    my=max(my,mp[zt]);
    cout<<mx*my;

}

幂次序列

点分治,先扫一边,将和维护到一个桶里,然后再扫另一边时在桶里查询,维护一个最高位,设为 \(p\),当前和为 \(sum\),只需要在桶里查询 \(2^p-sum\) 的个数,这是针对于两侧的,需要卡卡常。

点击查看代码
// ubsan: undefined
// accoders
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2*1e5+10;
const int mod1=998244353;
const int mod2=1e9+3579;
int a[N];
map<pair<int,int>,int> mp1;
map<pair<int,int>,int> mp2;
int ans=0;
int ha[N],hb[N];
int ma[N],mb[N];
inline int mgml(int x,int p,int mod){
    int ans=1;
    while(p){
        if(p&1) ans=(ans*x)%mod;
        x=(x*x)%mod;
        p>>=1;
    }
    return ans;
}
map<int,int> vi;
void solve(int l,int r){
    if(l==r){
        ans++;
        return;
    }
    int mid=(l+r)/2;
    int sum1=0,sum2=0;
    int ma=0;
    mp1.clear(),mp2.clear();
    vi.clear();
    for(int i=mid;i>=l;--i){
        sum1=(sum1+ha[i])%mod1;
        sum2=(sum2+hb[i])%mod2;
        int tmp=a[i];
        while(vi[tmp]){
            vi[tmp]=0;
            tmp++;
        }
        vi[tmp]=1;
        ma=max(tmp,ma);
        mp1[make_pair(sum1,sum2)]++;
        int w1=(mgml(2,ma+1,mod1)-sum1+mod1)%mod1;
        int w2=(mgml(2,ma+1,mod2)-sum2+mod2)%mod2;
        if(w1==sum1 && w2==sum2) continue;
        mp2[make_pair(w1,w2)]++;
    }
    sum1=0,sum2=0;
    int cnt=0;
    ma=0;
    vi.clear();
    for(int i=mid+1;i<=r;++i){
        sum1=(sum1+ha[i])%mod1;
        sum2=(sum2+hb[i])%mod2;
        int tmp=a[i];
        while(vi[tmp]){
            vi[tmp]=0;
            tmp++;
        }
        vi[tmp]=1;
        ma=max(ma,tmp);
        int w1=(mgml(2,ma+1,mod1)-sum1+mod1)%mod1;
        int w2=(mgml(2,ma+1,mod2)-sum2+mod2)%mod2;
        cnt+=mp1[make_pair(w1,w2)];
        cnt+=mp2[make_pair(sum1,sum2)];
    }
    ans+=cnt;
    solve(l,mid),solve(mid+1,r);
}
inline int read(){
	int x(0);bool f(0);char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar()) f^=ch=='-';
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
	return f?x=-x:x;
}
signed main(){
    freopen("sequence.in","r",stdin);
    freopen("sequence.out","w",stdout);
    int n;
    n=read();
    for(int i=1;i<=n;++i) a[i]=read();
    for(int i=1;i<=n;++i){
        ha[i]=mgml(2,a[i],mod1);
        hb[i]=mgml(2,a[i],mod2);
    }
    solve(1,n);
    printf("%lld",ans);
}

CSP模拟58联测20

正睿=罚坐

回忆旅途的过往

因为只有 \(10\) 个数,所以可以压一个状态,还有修改,那么直接线段树维护。

考场主要是如何快速求出是否可以表示没想到,实际上是预处理,设 \(dp_{s,j}\) 表示状态为 \(s\) 是否可以凑出 \(j\),考虑转移:

\(f_{S}=f_{S-(1<<id}\) ,\(f_{S,i}=f_{S,i}|f_{S,i-x}\)。但是注意枚举顺序,先枚举第几个数,然后最大状态就确定,再大的后面会计算。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int M=1e5+10;
int a[N];
int st[20];
bool f[2050][M];
struct asd{
    int op,l,r,x;
}b[N];
int rk[N];
struct tree{
    int l,r,zt;
    int lazy;
}tr[N*4];
void build(int p,int l,int r){
    tr[p].l=l,tr[p].r=r;
    tr[p].lazy=0;
    if(l==r){
        tr[p].zt=(1<<(rk[a[l]]-1));
        return;
    }
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    tr[p].zt=tr[p*2].zt|tr[p*2+1].zt;
}
void pushdown(int p){
    if(tr[p].lazy){
        tr[p*2].lazy=tr[p].lazy;
        tr[p*2+1].lazy=tr[p].lazy;
        tr[p*2].zt=tr[p].lazy;
        tr[p*2+1].zt=tr[p].lazy;
        tr[p].lazy=0;
    }
}
void change(int p,int l,int r,int v){
    if(tr[p].l>=l && tr[p].r<=r){
        tr[p].zt=(1<<(rk[v]-1));
        tr[p].lazy=(1<<(rk[v]-1));
        return;
    }
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)/2;
    if(l<=mid) change(p*2,l,r,v);
    if(r>mid) change(p*2+1,l,r,v);
    tr[p].zt=tr[p*2].zt|tr[p*2+1].zt;
}
int ask(int p,int l,int r){
    if(tr[p].l>=l && tr[p].r<=r) return tr[p].zt;
    pushdown(p);
    int mid=(tr[p].l+tr[p].r)/2;
    int ans=0;
    if(l<=mid) ans=ans|ask(p*2,l,r);
    if(r>mid) ans=ans|ask(p*2+1,l,r);
    return ans;
}
signed main(){
    // freopen("past3.in","r",stdin);
    // freopen("1.out","w",stdout);
    int n,m,q;
    scanf("%d%d%d",&n,&m,&q);
    int top=0;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        if(rk[a[i]]==0){
            st[++top]=a[i];
            rk[a[i]]=top;
        }
    }
    int ma=0;
    for(int i=1;i<=q;i++){
        scanf("%d%d%d%d",&b[i].op,&b[i].l,&b[i].r,&b[i].x);
        if(b[i].op==1){
            if(rk[b[i].x]==0){
                st[++top]=b[i].x;
                rk[b[i].x]=top;
            }
        }
        else ma=max(ma,b[i].x);
    }
    build(1,1,n);
    f[0][0]=1;
    for(int i=0;i<top;i++){
        int t=(1<<i);
        for(int s=0;s<t;s++){
            f[s][0]=1;
            for(int k=0;k<=ma;k++) f[s+t][k]|=f[s][k];
            for(int k=0;k<=ma-st[i+1];k++) f[s+t][k+st[i+1]]|=f[s+t][k];
        }
    }
    for(int i=1;i<=q;i++){
        if(b[i].op==1){
            change(1,b[i].l,b[i].r,b[i].x);
        }
        if(b[i].op==2){
            int s=ask(1,b[i].l,b[i].r);
            if(f[s][b[i].x]) printf("Yes\n");
            else printf("No\n");
        }
    }
}
/*
如何判断是否可行
可以用完全背包,但是复杂度炸裂
肯定枚举那十个数,然后判断是否在区间出现
如何判断可行,感觉就是背包,但是会有一些优化
比如当前点不行就终止
最外层是物品数量,然后是容量,最后是个数
判断可行直接枚举当前物品倍数判断,
*/

牵着她的手

首先一个结论是的东西,前 \(n\) 个和后 \(m\) 个最大值相等。

等同于求 \(\sum_{i=1}^{k} (i^n-(i-1)^n) \times (i^m-(i-1)^m)\)

然后就是:拉格朗日插值。

就结束了。

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

using namespace std;
const int N=1e5+10;
const int mod=1e9+7;
int n,m,k;
int fac[N*2+10],inv[N*2+10];
int y[N*2+10];
int mgml(int x,int p){
    int ans=1;
    while(p){
        if(p&1) ans=(ans*x)%mod;
        x=(x*x)%mod;
        p>>=1;
    }
    return ans;
}
signed main(){
    int T;
    scanf("%lld",&T);
    fac[0]=1,inv[0]=1;
    for(int i=1;i<=N*2;i++){
        fac[i]=fac[i-1]*i%mod;
        inv[i]=mgml(fac[i],mod-2);
    }
    while(T--){
        scanf("%lld%lld%lld",&n,&m,&k);
        int tmp=1;
        for(int i=1;i<=n+m+2;i++) tmp=tmp*(k-i)%mod;
        for(int i=1;i<=n+m+2;i++){
           y[i]=(y[i-1]+(mgml(i,n)-mgml(i-1,n)+mod)*(mgml(i,m)-mgml(i-1,m)+mod)%mod)%mod;
        }
        int ans=0;
        for(int i=1;i<=n+m+2;i++){
            int w=y[i]*tmp%mod*mgml(k-i,mod-2)%mod*inv[i-1]%mod*inv[n+m+2-i]%mod;
            if((n+m+2-i)%2) w=-w;
            ans=(ans+w+mod)%mod;
        }
        if(k<=n+m+2) ans=y[k];
        printf("%lld\n",ans);
    }
}

/*
行与列是有关系的,所以列的最大值不可以超过行的最大值
正睿=罚坐
前后两半的最大值相等,剩下就没有限制了?
确实,所以是不是可以容斥
其实就是随便选的方案减去最大值不相等的方案
*/

注视一切的终结

挺好一倍增,设 \(dp_{x,i,a,b}\) 表示从 \(x\) 点向上跳 \(2^{i-1}\) 步到达的点,\(a\)\(x\) 点上方颜色, \(b\) 为顶端点上方颜色。

还有就是如果颜色大于 \(3\),那么只需要存三个,设 \(col_{i,0/1/2/3}\) 表示个数和三个颜色。

然后就是转移:\(f_{x,i}\) 表示倍增父亲,设 \(y=f_{x,j-1},z=f_{x,j}\) \(a\)\(x\) 上方第几个颜色,\(b\)\(y\) 上方第几个颜色,\(c\)\(z\) 上方第几个颜色。 直接 \(f_{x,j,a,c}=max(f_{x,j-1,a,b}+f_{y,j-1,b,c})\)

查询时转移也是类似的,不是一样的。

尽量不要用 \(mp\)\(pair\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=5*1e5+2;
int head[N],ver[N*2],nex[N*2],tot=0;
unordered_map<int,int> rk[N];
inline void add(int x,int y){
    ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
}
vector<int> s[N];
unordered_map<int,int> mp[N];
int f[N][21];
int dp[N][21][4][4];
int col[N][4];
int d[N];
int t;
int n,m;
void dfs(int x,int fa){
    d[x]=d[fa]+1;
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==fa) continue;
        f[y][0]=x;
        int p=rk[x][y];
        int len=s[p].size();
        col[y][0]=min(3,len);
        for(int j=1;j<=col[y][0];j++){
            col[y][j]=s[p][j-1];
        }
        dfs(y,x);
    }
}
void init(){
    t=log2(n)+1;
    for(int j=1;j<=t;++j)
        for(int i=1;i<=n;++i)
            f[i][j]=f[f[i][j-1]][j-1];
    for(int x=1;x<=n;++x)
        for(int j=1;j<=col[x][0];++j)
            for(int k=1;k<=col[f[x][0]][0];++k)
                dp[x][0][j][k]=(col[x][j]!=col[f[x][0]][k]);
    for(int x=1;x<=n;++x){
        for(int i=1;i<=t;++i){
            int y=f[x][i-1],z=f[x][i];
            for(int k1=1;k1<=col[x][0];++k1){
                for(int k2=1;k2<=col[y][0];++k2){
                    for(int k3=1;k3<=col[z][0];++k3){
                        dp[x][i][k1][k3]=max(dp[x][i][k1][k3],dp[x][i-1][k1][k2]+dp[y][i-1][k2][k3]);
                    }
                }
            }
        }
    }
}
inline int ask_lca(int x,int y){
    if(d[x]>d[y]) swap(x,y);
    for(int i=t;i>=0;--i){
        if(d[f[y][i]]>=d[x]) y=f[y][i];
    }
    if(x==y) return x;
    for(int i=t;i>=0;--i){
        if(f[x][i]!=f[y][i]){
            x=f[x][i],y=f[y][i];
        }
    }
    return f[x][0];
}
int cx[4],cy[4];
inline void solve1(int &x,int top){
    memset(cx,0,sizeof(cx));
    for(int i=t;i>=0;--i){
        if(d[f[x][i]]>d[top]){
            int y=f[x][i];
            int cz[4];
            memset(cz,0,sizeof(cz));
            for(int k2=1;k2<=col[y][0];++k2){
                for(int k1=1;k1<=col[x][0];++k1){
                    cz[k2]=max(cz[k2],cx[k1]+dp[x][i][k1][k2]);
                }
            }
            for(int k2=1;k2<=col[y][0];++k2){
                cx[k2]=cz[k2];
            }
            x=f[x][i];
        }
    }
    return;
}
inline void solve2(int &x,int top){
    memset(cy,0,sizeof(cy));
    for(int i=t;i>=0;--i){
        if(d[f[x][i]]>d[top]){
            int y=f[x][i];
            int cz[4];
            memset(cz,0,sizeof(cz));
            for(int k2=1;k2<=col[y][0];++k2){
                for(int k1=1;k1<=col[x][0];++k1){
                    cz[k2]=max(cz[k2],cy[k1]+dp[x][i][k1][k2]);
                }
            }
            for(int k2=1;k2<=col[y][0];++k2){
                cy[k2]=cz[k2];
            }
            x=f[x][i];
        }
    }
    return;
}
inline int read(){
	int x(0);bool f(0);char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar()) f^=ch=='-';
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
	return f?x=-x:x;
}
inline void write(int x){
	x<0?x=-x,putchar('-'):0;static short Sta[50],top(0);
	do{Sta[++top]=x%10;x/=10;}while(x);
	while(top) putchar(Sta[top--]|48);
	putchar('\n');
}
signed main(){
    n=read(),m=read();
    int cnt=0;
    for(int i=1;i<=m;++i){
        int x,y,w;
        x=read(),y=read(),w=read();
        if(rk[x][y]==0){
            cnt++;
            rk[x][y]=cnt;
            rk[y][x]=cnt;
            add(x,y),add(y,x);
        }
        int p=rk[x][y];
        if(mp[p][w]==0){
            s[p].push_back(w);
            mp[p][w]=1;
        }
    }
    dfs(1,0);
    init();
    int q;
    q=read();
    while(q--){
        int x,y;
        x=read(),y=read();
        if(x==y){
            printf("0\n");
            continue;
        }
        int lca=ask_lca(x,y);
        int ans=0;
        if(x!=lca) solve1(x,lca);
        if(y!=lca) solve2(y,lca);
        if(x==lca) for(int i=1;i<=col[y][0];++i) ans=max(ans,cy[i]);
        else if(y==lca) for(int i=1;i<=col[x][0];++i) ans=max(ans,cx[i]);
        else{
            for(int i=1;i<=col[x][0];++i){
                for(int j=1;j<=col[y][0];++j){
                    ans=max(ans,cx[i]+cy[j]+(col[x][i]!=col[y][j]));
                }
            }
        }
        write(ans);
    }
}
posted @ 2023-10-08 07:37  _bloss  阅读(67)  评论(0)    收藏  举报