CSP 模拟 3

今天感觉很热,但是天气转凉的时候我也该退役了吧。

今日推歌:

透明哀歌 - n-buna / Gumi
echo - Crusher-P / Gumi English

歌词 The clock stopped ticking,时钟停止发出嘀嗒声

Forever ago.在很久以前

How long have I been up?我究竟醒来了多久?

I don't know.我不知道

I can't get a grip,我无法紧紧抓住

But I can't let go却也无法放手

There wasn't anything(虽然如此)却没有任何东西

To hold on to though...让我紧紧抓住...

Why can't I see?为何我看不见?

Why can't I see?为何我看不见?

All the colors that you see?你所看到的全部色彩?

Please, Can I be,请问,我能变得

Please, Can I be请问,我能够变得

Colorful and free?色彩斑斓和自由吗?

WHAT THE HELL'S GOING ON?!究竟发生了什么事?!

CAN SOMEONE TELL ME PLEASE--求求你,谁能告诉我--

WHY, I'M SWITCHING FASTER THAN THE CHANNELS ON TV!!为何我切换得比电视上的频道还要快!!

I'M black, THEN I'M white!!我是黑色,然后我是白色!!

NO!!!不!!!

SOMETHING ISN'T RIGHT!!有什么不对劲!!

MY ENEMY'S INVISIBLE, I DON'T KNOW HOW TO FIGHT!!我的敌人是透明的,我不知道如何去战斗!!

THE TREMBLING FEAR IS MORE THAN I CAN TAKE,颤栗着的恐惧已经超出我的承受能力

WHEN I'M UP AGAINST当我与镜子里的

THE ECHO IN THE MIRROR!!回声对抗!!

ECHO!!回声!!

I'm gonna burn my house down,我会把我的房子烧成,

Into an ugly black.一片丑陋的黑色。

I'm gonna run away, Now我现在就会逃跑

And never look back.然后再也不回头

I'm gonna burn my house down,我会把我的房子烧成

Into an ugly black.丑陋的黑色

I'm gonna run away, Now我现在就要逃跑

And never look back.然后再也不回头

I'm gonna burn my house down,我现在就要把我的房子烧成

Into an ugly black.丑陋的黑色

I'm gonna run away, Now我现在就要逃跑

And never look back.然后再也不回头

I'm gonna burn my house down,我现在就要把我的房子烧成

Into an ugly black.丑陋的黑色

I'm gonna run away, Now我现在就要逃跑

And never look back.然后再也不回头

I'm gonna burn my house down,我现在就要把我的房子烧毁,

Into an ugly black.丑陋的黑色

I'm gonna run away, Now我现在就要逃跑

AND NEVER LOOK BACK!!然后再也不回头!!

WHAT THE HELL'S GOING ON?!究竟是怎么回事?!

CAN SOMEONE TELL ME PLEASE--求求你,告诉我好吗--

WHY, I'M SWITCHING FASTER THAN THE CHANNELS ON TV!!为何我切换得比电视频道都要快

I'M black, THEN I'M white!!我是黑色 而后变成白色

NO!!!不!!!SOMETHING ISN'T RIGHT!!有什么不对劲!!

MY ENEMY'S INVISIBLE, I DON'T KNOW HOW TO FIGHT!!我的敌人是无形的,我不知道要如何去战斗!!

WHAT THE HELL'S GOING ON?!究竟是怎么回事?!

CAN SOMEONE TELL ME PLEASE--求求你,谁能告诉我--

WHY, I'M SWITCHING FASTER THAN THE CHANNELS ON TV!!为何我切换得比电视上的频道还要快!!

I'M black, THEN I'M white!!我是黑色,然后我是白色!!

NO!!!不!!!

SOMETHING ISN'T RIGHT!!有什么不对劲!!

MY ENEMY'S INVISIBLE, I DON'T KNOW HOW TO FIGHT!!我的敌人是透明的,我不知道要如何去战斗

THE TREMBLING FEAR IS MORE THAN I CAN TAKE,颤栗着的恐惧已超出我的承受能力

WHEN I'M UP AGAINST当我与镜子里的

THE ECHO IN THE MIRROR!!回声对抗!!

THE TREMBLING FEAR IS MORE THAN I CAN TAKE,颤栗着的恐惧已超出我的承受能力,

WHEN I'M UP AGAINST当我与镜子里的

THE ECHO IN THE MIRROR!!回声对抗!!

T1 回文

简单题,但是赛时就是死活想不到,因为走的步数是固定的,所以同时从起点和终点走,枚举从起点和终点分别走到哪里,因为一共走的步数是一定的,所以可以把四个状态化成三个状态,然后大力 dp 即可

这题有点卡常+卡空间,在这之前我都不敢想 \(O(n^3)\) 时空复杂度过 500

点击查看代码
#include<bits/stdc++.h>
// #define int long long
#define N 505
using namespace std;

const int p=993244853;
int n,m,f[N][N][N];
long long ans;
char s[N][N];

signed main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        scanf("%s",s[i]+1);
    f[1][1][n]=(s[1][1]==s[n][m]);
    if(!f[1][1][n]){
        printf("0\n");
        return 0;
    }
    for(int x1=1;x1<=n;x1++)
        for(int y1=1;y1<=m;y1++)
            for(int x2=min(n,n+m+2-x1-y1);x2>=x1&&n+m+2-x1-y1-x2<=m;x2--){
                int y2=n+m+2-x1-y1-x2;
                if(s[x1][y1]==s[x2][y2]) f[x1][y1][x2]=((long long)f[x1][y1][x2]+f[x1-1][y1][x2+1]+f[x1-1][y1][x2]+f[x1][y1-1][x2+1]+f[x1][y1-1][x2])%p;
            }
    for(int x1=1;x1<=n;x1++)
        for(int y1=1;y1<=m;y1++){
            int x2=x1,y2=y1; 
            if(x1+x2+y1+y2==n+m+2) ans+=f[x1][y1][x2];
            x2=x1+1,y2=y1;
            if(x1+x2+y1+y2==n+m+2) ans+=f[x1][y1][x2];
            x2=x1,y2=y1+1;
            if(x1+x2+y1+y2==n+m+2) ans+=f[x1][y1][x2];
            ans%=p;
        }
    cout<<ans;
}

T2 快速排序

简单题,不难发现,只有正常数字才会参与排序,然后每个数字会将所有还没被移动比自己小的的数字按顺序放到自己后边,只要将所有数字存下来排序然后跑一遍即可

点击查看代码
#include<bits/stdc++.h>
#define N 500005
using namespace std;

int T,n,top,stk[N];
struct number{
	bool isnan;
	int value;
}A[N];
bool operator < (const number& x, const number& y){
	if(x.isnan || y.isnan) return false;
	return x.value < y.value;
}

int main(){
    // freopen("qsort2.in","r",stdin);
    // freopen("qsort.out","w",stdout);
    cin>>T;
    while(T-->0){
        scanf("%d",&n);
        int flag=true;top=0;
        for(int i=1;i<=n;i++){
            char tmp[15];
            scanf("%s",tmp+1);
            A[i].isnan=(tmp[1]=='n');
            if(A[i].isnan) continue;
            int len=strlen(tmp+1);
            for(int j=1;j<=len;j++)
                A[i].value=A[i].value*10+(tmp[j]-'0');
            stk[++top]=A[i].value;
        }
        sort(stk+1,stk+1+top);
        int lst=1,maxn=0;
        for(int i=1;i<=n;i++){
            if(A[i].isnan) printf("nan ");
            else{
                if(maxn<=A[i].value){
                    int now=lower_bound(stk+lst,stk+top+1,A[i].value)-stk;
                    for(int j=lst;j<=now;j++) printf("%d ",stk[j]);
                    lst=now+1;
                }
                maxn=max(A[i].value,maxn);
            }
            A[i].value=0;
        }
        printf("\n");
    }
}

T3 混乱邪恶

构造,先将 \(a\) 升序排序,然后设 \(d_i=a_{2i}-a_{2i-1}\)

然后按如下方案构造:

重复进行如下操作直到 \(n=1\)

  • 取出集合中的 \(\min d\)\(\max d\)
  • 将 $\max d-\min d $ 插入集合中
  • \(n=n-1\)

如果对于一个 \(\{d_n\}\) 来说存在一个 \(\{e_n\}\) 使得 \(\sum d_ie_i=0\),那么构造出 \(\{d_n\}\)\(\{d'_n\}\) 也有一个确定的 \(\{e'_n\}\)

对于 \(n=1\) 的情况,\(e_1=1\) 或者 \(-1\) 都可以,然后对于每个操作来说,建一个新节点表示 \(\max d-\min d\),然后向 \(\min d\) 连一条权值为 \(-1\) 的边,向 \(\max d\) 连一条权值为 \(1\) 的边,对于初始的 \(d_i\) 来说,建一条权值 \(-1\) 的边向 \(2i\),建一条权值 \(1\) 的边向 \(2i-1\),然后从 \(n=1\) 的情况向下走,每个点的取值即为从当前点到根上路径的乘积

点击查看代码
#include<bits/stdc++.h>
#define N 3000005
#define int long long
#define pii pair<int,int>
#define mp make_pair
using namespace std;

int n,m,a[N],c[N],rollingstar;
int tot,tree[N][2],node[N];
int stk[N],top;
multiset<pii> ms;
void solve(int n){
    if(n==1) return;
    auto mx=--ms.end(),mn=ms.begin();
    pii maxn=*mx,minn=*mn;
    ms.erase(mx);ms.erase(mn);
    tree[++tot][0]=maxn.second;
    tree[tot][1]=minn.second;
    // cout<<tot<<" "<<tree[tot][0]<<" "<<tree[tot][1]<<endl;
    ms.insert(mp(maxn.first-minn.first,tot));
    solve(n-1);
}
void dfs(int u,int num);

signed main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) scanf("%lld",a+i);
    if(n%2) a[++n]=0,rollingstar++;
    for(int i=1;i<=n;i++) c[a[i]]=i;
    sort(a+1,a+1+n);tot=n;
    for(int i=1;i<=n;i+=2){
        tree[++tot][0]=c[a[i+1]],tree[tot][1]=c[a[i]],
        ms.insert(mp(a[i+1]-a[i],tot));
        // cout<<c[a[i]]<<" "<<c[a[i+1]]<<endl;
    }
    solve(n/2);dfs(tot,0);
    printf("NP-Hard solved\n");
    for(int i=1;i<=n-rollingstar;i++) printf("%lld ",node[i]);
}

void dfs(int u,int num){
    // cout<<u<<" "<<node[u]<<endl;
    if(tree[u][0]<=n){
        node[tree[u][0]]=num?-1:1;
        node[tree[u][1]]=num?1:-1;
        return;
    }
    dfs(tree[u][0],num);
    dfs(tree[u][1],num^1);
}

T4 校门外歪脖树上的鸽子

nb ds 题

考虑在原本的树上向右上走的意义为让节点管辖区间的右端点扩张,向左上走的意义类似

\([l,r]\) 的操作化为 \(l\)\(\operatorname{lca}(l,r)\) 的操作和 \(r\)\(\operatorname{lca}(l,r)\) 的操作

现在我们只考虑左端点链上的情况

首先我们令 \(u\)\(l\) 节点向右上跳的最远位置,这表示让以 \(l\) 为左端点的区间尽可能的大

特殊情况

如果 \(u\)\(\operatorname{lca}\) 祖先,说明只需要操作 \(\operatorname{lca}\) 的左儿子即可

需要注意的是,如果左右都是这种情况,说明只需要操作 \(\operatorname{lca}\) 即可

\(u\) 是最远位置说明向右上跳不动了,那么左上还能跳,但是如果向左上跳的话说明左端点会扩张,不成立,所以考虑寻找下一个节点,\(u\) 的祖先肯定是不行了,但是 \(u\) 的父亲的兄弟是可行的,所以这是第二个节点,所以就抽象为这条链上所有与 \(l\) 节点同侧但父亲不同侧的节点,其最终会跳到 \(\operatorname{lca}\) 的右儿子,那么我们 dfs 一遍将满足这个关系的节点求出来并连边,操作就化为了这个新树上的树链剖分

写代码的时候复刻了题目的线段树:

void pushdown(int pos,int l,int r){
    if(lz[pos]) return;
    sum[lc]+=(cnt[mid]-cnt[l-1])*lz[pos];
    sum[rc]+=(cnt[r]-cnt[mid])*lz[pos];
    lz[lc]+=lz[pos];
    lz[rc]+=lz[pos];
    lz[pos]=0;
}
点击查看代码
#include<bits/stdc++.h>
#define N 500005
#define int long long 
#define pii pair<int,int>
#define lc (pos<<1)
#define rc ((pos<<1)|1)
#define inf 0x3f3f3f3f
#define mid ((l+r)>>1)
#define mp make_pair
using namespace std;

int head[N],tot,total;
struct edge{
    int v,nxt;
}e[N*2];
inline void add(int u,int v){
    e[++tot].v=v;
    e[tot].nxt=head[u];
    head[u]=tot;
}
int n,m,tree[N][2],rt,siz[N],Cnt[N],hson[N],top[N],dfn[N];
int dep[N],f[N][21],vis[N],bro[N],fa[N],cnt[N];
void Dfs(int u){ // 原树上 dfs 一遍预处理倍增数组
    for(int i=1;i<=20;i++)
        f[u][i]=f[f[u][i-1]][i-1];
    if(!tree[u][0]) {Cnt[u]=1;return;}
    else{
        f[tree[u][0]][0]=f[tree[u][1]][0]=u;
        dep[tree[u][0]]=dep[tree[u][1]]=dep[u]+1;
        Dfs(tree[u][0]);Dfs(tree[u][1]);
        Cnt[u]=Cnt[tree[u][0]]+Cnt[tree[u][1]];
    }
}
void Dfs1(int u,int top){ // 原树上 dfs 求能跳到右上的最远点的兄弟
    if(!tree[u][0]) return;
    add(tree[u][0],top);add(top,tree[u][0]);
    Dfs1(tree[u][0],top);
    Dfs1(tree[u][1],tree[u][0]);
}
void Dfs2(int u,int top){ // 原树上 dfs 求能跳到左上的最远点的兄弟
    if(!tree[u][0]) return;
    add(tree[u][1],top);add(top,tree[u][1]);
    Dfs2(tree[u][0],tree[u][1]);
    Dfs2(tree[u][1],top);
}
int lca(int x,int y){
    // cout<<"in:"<<x<<" "<<y<<endl;
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=20;i>=0;i--){
        if(dep[f[x][i]]>=dep[y]) x=f[x][i];
        if(x==y) return x;
    }
    for(int i=20;i>=0;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    // cout<<"out:"<<x<<" "<<y<<endl; 
    return f[x][0];
}
struct SMT{
    int sum[N<<2],lz[N<<2];
    void pushdown(int pos,int l,int r){
        if(!lz[pos]) return;
        sum[lc]+=(cnt[mid]-cnt[l-1])*lz[pos];
        sum[rc]+=(cnt[r]-cnt[mid])*lz[pos];
        lz[lc]+=lz[pos];
        lz[rc]+=lz[pos];
        lz[pos]=0;
    }
    void update(int pos,int l,int r,int L,int R,int val){
        if(r<L||R<l) return;
        if(L<=l&&r<=R){
            sum[pos]+=(cnt[r]-cnt[l-1])*val;
            lz[pos]+=val;
            return;
        }
        pushdown(pos,l,r);
        update(lc,l,mid,L,R,val);
        update(rc,mid+1,r,L,R,val);
        sum[pos]=sum[lc]+sum[rc];
    }
    int query(int pos,int l,int r,int L,int R){
        // cout<<pos<<" "<<l<<" "<<r<<" "<<L<<" "<<R<<endl;
        if(r<L||R<l) return 0;
        if(L<=l&&r<=R) return sum[pos];
        pushdown(pos,l,r);
        return query(lc,l,mid,L,R)+query(rc,mid+1,r,L,R);
    }
}T;
// 熟练剖分
void dfs1(int u,int p){
    int maxn=0;
    fa[u]=p;siz[u]=1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].v;
        if(v==p) continue;
        dfs1(v,u);
        siz[u]+=siz[v];
        if(maxn<siz[v]){
            maxn=siz[v];
            hson[u]=v;
        }
    }
}
void dfs2(int u,int anc){
    top[u]=anc;cnt[++total]=Cnt[u];dfn[u]=total;
    if(hson[u]) dfs2(hson[u],anc);
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].v;
        if(v==fa[u]||v==hson[u]) continue;
        dfs2(v,v);
    }
}
void updateChain(int x,int y,int val){
    while(top[x]!=top[y]){
        T.update(1,1,2*n-1,dfn[top[x]],dfn[x],val);
        x=fa[top[x]];
    }
    T.update(1,1,2*n-1,dfn[y],dfn[x],val);
    T.update(1,1,2*n-1,dfn[y],dfn[y],-val);
}
int queryChain(int x,int y){
    int ans=0;
    while(top[x]!=top[y]){
        // cout<<x<<endl;
        ans+=T.query(1,1,2*n-1,dfn[top[x]],dfn[x]);
        x=fa[top[x]];
    }
    ans+=T.query(1,1,2*n-1,dfn[y],dfn[x]);
    ans-=T.query(1,1,2*n-1,dfn[y],dfn[y]);
    return ans;
}
#define is(u) (tree[f[u][0]][1]==u)
void Update(int l,int r,int val){
    int tmp=lca(l,r);
    if(!is(l)) l=bro[fa[l]];
    if(is(r)) r=bro[fa[r]];
    if(dep[l]<=dep[tmp]&&dep[r]<=dep[tmp]){ T.update(1,1,2*n-1,dfn[tmp],dfn[tmp],val);return;}
    if(dep[l]<=dep[tmp]) T.update(1,1,2*n-1,dfn[tree[tmp][0]],dfn[tree[tmp][0]],val);
    else updateChain(l,tree[tmp][1],val);
    if(dep[r]<=dep[tmp]) T.update(1,1,2*n-1,dfn[tree[tmp][1]],dfn[tree[tmp][1]],val);
    else updateChain(r,tree[tmp][0],val);
}
int Query(int l,int r){
    int tmp=lca(l,r),ans=0;
    if(!is(l)) l=bro[fa[l]];
    if(is(r)) r=bro[fa[r]];
    if(dep[l]<=dep[tmp]&&dep[r]<=dep[tmp]) return T.query(1,1,2*n-1,dfn[tmp],dfn[tmp]);
    if(dep[l]<=dep[tmp]) ans+=T.query(1,1,2*n-1,dfn[tree[tmp][0]],dfn[tree[tmp][0]]);
    else ans+=queryChain(l,tree[tmp][1]);
    if(dep[r]<=dep[tmp]) ans+=T.query(1,1,2*n-1,dfn[tree[tmp][1]],dfn[tree[tmp][1]]);
    else ans+=queryChain(r,tree[tmp][0]);
    return ans;
}

signed main(){
    // freopen("pigeons2.in","r",stdin);
    // freopen("pigeons.out","w",stdout);
    cin>>n>>m;
    for(int i=n+1;i<=2*n-1;i++){
        int Lc,Rc;scanf("%lld%lld",&Lc,&Rc);
        tree[i][0]=Lc;tree[i][1]=Rc;
        vis[Lc]=vis[Rc]=true;
        bro[Lc]=Rc;bro[Rc]=Lc;
    }
    for(int i=n+1;i<n*2;i++) if(!vis[i]) rt=i;
    dep[rt]=1;
    Dfs(rt);Dfs1(rt,rt);Dfs2(rt,rt);
    dfs1(rt,0);dfs2(rt,rt);
    for(int i=1;i<n*2;i++) cnt[i]+=cnt[i-1];
    while(m-->0){
        int op;scanf("%lld",&op);
        if(op==1){
            int l,r,x;
            scanf("%lld%lld%lld",&l,&r,&x);
            Update(l,r,x);
        }
        else{
            int l,r;
            scanf("%lld%lld",&l,&r);
            printf("%lld\n",Query(l,r));
        }
    }
}
posted @ 2023-07-23 16:28  Rolling_star  阅读(61)  评论(2编辑  收藏  举报