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

csp模拟50#

异或#

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

考虑一个数 x 做出的贡献是这个数抑或上 x+1 也就是这个数二进制拆分下末尾连续1的长度加 1,所以直接数位dp,
len 表示长度,若这位为1len+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 等于 (depa+y)modx(下文设其为 k)的节点。这样我们就可以把修改转化为将一个点的子树内所有深度模 xk 的节点权值加上 z

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

再考虑 x<n ,此时暴跳会使复杂变成 O(n) ,所以根号分治,设 DS2x,k,i 表示深度 modxk 的在第 i 块的变化,复杂度 n, 查 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#

集合#

背包,求出每种数出现的次数,但是幂数很大,但是费马小定理,模数是质数, xp11modp(p) ,所以摸 p1

点击查看代码
// 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(rl+1+d) ,将常数放到一边,得到 lrvalik<kd 所以可以求最长连续子段长度是否大于 kd ,若大于则 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,那么存在的方案为 (i/2+ji)

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

dpi,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,设 dpi,j 表示以 i 为根的树中,限制为 j 的最大值,如果没限制则为 0
复杂度 O(nk)

点击查看代码
// 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,设 dpi,j 表示二进制第 j 位为 1 能到达 i 的最大的点。考虑转移,设 fi,j 表示 i 之前二进制第 j 位为 1 最大点。然后

dpi,j=max(fi,j) 此时 ai 必须二进制第 j 位为 1

dpi,j=max(dpfi,k,j) 此时 ai 必须二进制第 k 位为 1

判断时,枚举 dpr,k al&(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 开始恢复到回满的最小时间。

如果要求一个区间内的点从一定时间从零开始回复的值,用主席树维护前缀和,相当于二维偏序,对于小于 tc 求和,对于大于等于 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#

传话游戏#

烂,哈希被卡 40pts

全球覆盖#

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

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

所以考虑排个序,从左到右,然后异或 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,只需要在桶里查询 2psum 的个数,这是针对于两侧的,需要卡卡常。

点击查看代码
// 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 个数,所以可以压一个状态,还有修改,那么直接线段树维护。

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

fS=fS(1<<id ,fS,i=fS,i|fS,ix。但是注意枚举顺序,先枚举第几个数,然后最大状态就确定,再大的后面会计算。

点击查看代码
#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 个最大值相等。

等同于求 i=1k(in(i1)n)×(im(i1)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);
    }
}

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

注视一切的终结#

挺好一倍增,设 dpx,i,a,b 表示从 x 点向上跳 2i1 步到达的点,ax 点上方颜色, b 为顶端点上方颜色。

还有就是如果颜色大于 3,那么只需要存三个,设 coli,0/1/2/3 表示个数和三个颜色。

然后就是转移:fx,i 表示倍增父亲,设 y=fx,j1,z=fx,j ax 上方第几个颜色,by 上方第几个颜色,cz 上方第几个颜色。 直接 fx,j,a,c=max(fx,j1,a,b+fy,j1,b,c)

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

尽量不要用 mppair

点击查看代码
#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);
    }
}

作者:bloss

出处:https://www.cnblogs.com/jinjiaqioi/p/17747963.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   _bloss  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu