冲刺国赛模拟 5

今天上午给我整不会了,开着空调结果冻的要死,这还是外边三十多度的情况下。结果一上午两眼发黑打题,心态相当爆炸。

题目名称好像是 qlr 洛谷主页。

今晚九点

简单题。为啥赛时没人做。

在每个位置有意义的走法显然只有两种:用 \(2\) 步走到相邻一格,或者用 \(1\) 步走到头。那每个点最多 \(8\) 条边连出去,直接跑最短路就行。

int n,m,num,id[1010][1010];
char s[1010][1010];
struct node{
    int v,w,next;
}edge[8000010];
int t,head[1000010];
void add(int u,int v,int w){
    edge[++t].v=v;edge[t].w=w;edge[t].next=head[u];head[u]=t;
}
struct stu{
    int x,w;
    bool operator<(const stu&s)const{
        return w>s.w;
    }
};
priority_queue<stu>q;
int dis[1000010];
bool vis[1000010];
void dijkstra(int st){
    memset(dis,0x3f,sizeof(dis));
    dis[st]=0;q.push({st,0});
    while(!q.empty()){
        int x=q.top().x;q.pop();
        if(!vis[x]){
            vis[x]=true;
            for(int i=head[x];i;i=edge[i].next){
                if(dis[edge[i].v]>dis[x]+edge[i].w){
                    dis[edge[i].v]=dis[x]+edge[i].w;
                    if(!vis[edge[i].v])q.push({edge[i].v,dis[edge[i].v]});
                }
            }
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%s",s[i]+1);
        for(int j=1;j<=m;j++)id[i][j]=++num;
    }
    for(int i=2;i<n;i++){
        int pre;
        for(int j=2;j<m;j++){
            if(s[i][j]=='#')continue;
            if(s[i][j]=='.'&&s[i][j-1]=='#')pre=j;
            if(j^pre)add(id[i][j],id[i][pre],1);
            if(s[i][j]=='.'&&s[i][j-1]=='.')add(id[i][j],id[i][j-1],2);
        }
        for(int j=m-1;j>=2;j--){
            if(s[i][j]=='#')continue;
            if(s[i][j]=='.'&&s[i][j+1]=='#')pre=j;
            if(j^pre)add(id[i][j],id[i][pre],1);
            if(s[i][j]=='.'&&s[i][j+1]=='.')add(id[i][j],id[i][j+1],2);
        }
    }
    for(int j=2;j<m;j++){
        int pre;
        for(int i=2;i<n;i++){
            if(s[i][j]=='#')continue;
            if(s[i][j]=='.'&&s[i-1][j]=='#')pre=i;
            if(i^pre)add(id[i][j],id[pre][j],1);
            if(s[i][j]=='.'&&s[i-1][j]=='.')add(id[i][j],id[i-1][j],2);
        }
        for(int i=n-1;i>=2;i--){
            if(s[i][j]=='#')continue;
            if(s[i][j]=='.'&&s[i+1][j]=='#')pre=i;
            if(i^pre)add(id[i][j],id[pre][j],1);
            if(s[i][j]=='.'&&s[i+1][j]=='.')add(id[i][j],id[i+1][j],2);
        }
    }
    int x1,y1,x2,y2;scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
    dijkstra(id[x1][y1]);
    if(dis[id[x2][y2]]==0x3f3f3f3f)dis[id[x2][y2]]=-1;
    printf("%d\n",dis[id[x2][y2]]);
    return 0;
}

小王唱歌

赛时冻的神志不清导致完美错过正解。

首先简单容斥一下,枚举边集。然后就变成了算每个连通块最小值的积。设 \(dp_{x,i}\)\(x\) 子树,\(x\) 所在连通块最小值为 \(i\) 的答案,再设个 \(g\) 为不算 \(x\) 连通块的答案。转移很简单,考虑加不加入连通块:

\[g_{x,i}=g_{x,i}\sum dp_{v,j}-g_{x,i}\sum_{j>i}g_{v,j}-g_{v,i}\sum_{j>i}g_{x,j}-g_{x,i}g_{v,i} \]

只有后缀和,冲一发线段树合并就行了。

因为 pushup 没取模调了一个小时,警钟长鸣。

const int mod=998244353;
int n,cnt,b[500010],lsh[500010];
struct gra{
    int v,next;
}edge[1000010];
int tot,head[500010];
void add(int u,int v){
    edge[++tot].v=v;edge[tot].next=head[u];head[u]=tot;
}
struct node{
    #define lson tree[rt].ls
    #define rson tree[rt].rs
    int ls,rs,f,g,lz;
}tree[500010<<5];
int t,rt[500010];
void pushup(int rt){
    tree[rt].f=(tree[lson].f+tree[rson].f)%mod;
    tree[rt].g=(tree[lson].g+tree[rson].g)%mod;
}
void pushtag(int rt,int val){
    tree[rt].f=1ll*tree[rt].f*val%mod;
    tree[rt].g=1ll*tree[rt].g*val%mod;
    tree[rt].lz=1ll*tree[rt].lz*val%mod;
}
void pushdown(int rt){
    if(tree[rt].lz!=1){
        if(lson)pushtag(lson,tree[rt].lz);
        if(rson)pushtag(rson,tree[rt].lz);
        tree[rt].lz=1;
    }
}
void update(int &rt,int L,int R,int pos){
    if(!rt)rt=++t,tree[rt].lz=1;
    if(L==R){
        tree[rt].g=1;tree[rt].f=lsh[L];return;
    }
    int mid=(L+R)>>1;
    if(pos<=mid)update(lson,L,mid,pos);
    else update(rson,mid+1,R,pos);
    pushup(rt);
}
int merge(int x,int y,int l,int r,int sumx,int sumy,int val){
    if(!x&&!y)return 0;
    if(!x){
        pushtag(y,(mod-sumx)%mod);
        return y;
    }
    if(!y){
        pushtag(x,(val-sumy+mod)%mod);
        return x;
    }
    if(l==r){
        int ret=tree[x].g;
        tree[x].g=1ll*tree[x].g*val%mod;
        tree[x].g=(tree[x].g-1ll*ret*sumy%mod+mod)%mod;
        tree[x].g=(tree[x].g-1ll*tree[y].g*sumx%mod+mod)%mod;
        tree[x].g=(tree[x].g-1ll*ret*tree[y].g%mod+mod)%mod;
        tree[x].f=1ll*tree[x].g*lsh[l]%mod;
        return x;
    }
    pushdown(x);pushdown(y);
    int mid=(l+r)>>1;
    tree[x].ls=merge(tree[x].ls,tree[y].ls,l,mid,(sumx+tree[tree[x].rs].g)%mod,(sumy+tree[tree[y].rs].g)%mod,val);
    tree[x].rs=merge(tree[x].rs,tree[y].rs,mid+1,r,sumx,sumy,val);
    pushup(x);
    return x;
}
void dfs(int x,int f){
    update(rt[x],1,cnt,b[x]);
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=f){
            dfs(edge[i].v,x);
            rt[x]=merge(rt[x],rt[edge[i].v],1,cnt,0,0,tree[rt[edge[i].v]].f);
        }
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&b[i]),lsh[i]=b[i];
    sort(lsh+1,lsh+n+1);
    cnt=unique(lsh+1,lsh+n+1)-lsh-1;
    for(int i=1;i<=n;i++)b[i]=lower_bound(lsh+1,lsh+cnt+1,b[i])-lsh;
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    dfs(1,0);
    printf("%d\n",tree[rt[1]].f);
    return 0;
}

不见不散

超级结论题。

首先如果操作数和逆序对数奇偶性不同显然无解。剩下的猜一下有解,开始构造。

设当前每个人有的礼物的排列为 \(p\),首先我们有显然的方法去掉一个 \(p_i\neq a_i\) 的数:把它和别的都交换一遍,并把 \(p_j=a_i\)\(j\) 放到最后交换。这样去掉所有 \(p_i\neq a_i\) 的数,剩下的数都是 \(p_i=a_i\)

考虑所有剩下的数,此时逆序对数为 \(0\),因此剩下的数个数一定满足 \(\bmod 4\equiv 0/1\)。那么四个四个去掉就行。

首先我们有方法用完两个数的操作次数并交换它们:设它们为 \(a,b\),先使 \(a\) 和除了 \(b\) 以外的所有数交换,然后交换 \(a,b\),最后把 \(b\) 按照 \(a\) 的操作顺序反过来操作。这样只有这两个数交换了,别的数都没有变化。那么四个数互相交换的方法是容易手模得到的,四个四个去掉就行了。

int n,cnt,p[1010],a[1010];
vector<int>S;
void get(int x,int y){
    if(x>y)swap(x,y);
    swap(p[x],p[y]);printf("%d %d\n",x,y);
}
void del(int x){
    for(vector<int>::iterator it=S.begin();it!=S.end();++it){
        if(*it==x){
            S.erase(it);break;
        }
    }
    int pos;
    for(int i:S)if(p[i]==a[x]){
        pos=i;break;
    }
    for(int i:S)if(pos!=i)get(x,i);
    get(x,pos);
}
void solve(int x,int y){
    for(int i:S)get(x,i);
    get(x,y);
    reverse(S.begin(),S.end());
    for(int i:S)get(y,i);
}
void calc(){
    int a=S.back();S.pop_back();
    int b=S.back();S.pop_back();
    int c=S.back();S.pop_back();
    int d=S.back();S.pop_back();
    solve(a,b);solve(c,d);
    get(a,c);get(b,d);get(a,d);get(b,c);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),p[i]=i,S.push_back(i);
    int cnt=0;
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            if(a[i]>a[j])cnt++;
        }
    }
    if((cnt^(n*(n-1)>>1))&1){
        puts("NO");return 0;
    }
    puts("YES");
    while(1){
        bool jud=true;
        for(int i:S){
            if(p[i]!=a[i]){del(i);jud=false;break;}
        }
        if(jud)break;
    }
    while(S.size()>1)calc();
    return 0;
}
posted @ 2023-05-17 16:50  gtm1514  阅读(28)  评论(0编辑  收藏  举报