首页 > 其他分享 >[41] (CSP 集训) CSP-S 模拟 9

[41] (CSP 集训) CSP-S 模拟 9

时间:2024-10-07 21:25:10浏览次数:1  
标签:CSP return int ts id pos ans 41 集训

A.邻面合并

观察到 \(m\) 很小,支持我们 \(mn2^{2m}\)

状压枚举二进制状态, \(f_{i,j,k}\) 表示到第 \(i\) 位的状态为 \(j\),上一位状态为 \(j\) (\(i,j\) 为状压位) 的方案数

考虑转移的时候加了什么

判断这一行与上一行的联通情况,如果这一行的某一个连通块和上一行正好对上了,那么就可以直接扩展矩形,答案不增加,否则对每个连通块,答案加一

我对这个联通情况的设计是不咋好的,还是说一下

设 \(a\) 是原矩阵第 \(i\) 行,\(b\) 是枚举到的状压状态,我判断连通块是根据

  • 连通块内不存在 \(a_j=0\) 的 \(j\)
  • 连通块内不存在 \(b_j=0\) 的 \(j\)

这么一做就会出很多冗余状态,但是我也不太会设计更好的了,所以就只能这样了

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[101][9];
int f[2][(1<<8)+1][(1<<8)+1];
int minn[101][(1<<8)+1];
vector<pair<int,int>>thi,las;
inline void work(int i,int j,vector<pair<int,int>>&ts){
    if(i==0) return;
    ts.clear();
    for(int k=1;k<=m;++k){
        int tmp=(j>>(k-1))&1;
        int tmplast=(k==1?tmp:(j>>(k-2))&1);
        if(a[i][k]==0){
            if(!ts.empty() and ts.back().second==0){
                ts.back().second=k-1;
            }
        }
        else if(k==1 or tmp!=tmplast or a[i][k-1]==0){
            if(!ts.empty() and ts.back().second==0){
                ts.back().second=k-1;
            }
            ts.push_back({k,0});
        }
    }
    if(!ts.empty() and ts.back().second==0){
        ts.back().second=m;
    }
}
int main(){
    freopen("merging.in","r",stdin);
    freopen("merging.out","w",stdout);
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            scanf("%d",&a[i][j]);
        }
    }
    memset(minn,0x3f,sizeof minn);
    memset(minn[0],0,sizeof minn[0]);
    for(int i=1;i<=n;++i){
        memset(f[i&1],0x3f,sizeof f[i&1]);
        for(int j=0;j<=(1<<m)-1;++j){
            for(int k=0;k<=(1<<m)-1;++k){
                work(i,j,thi);
                work(i-1,k,las);
                int res=0;
                for(auto p:thi){
                    res++;
                    for(auto q:las){
                        if(p==q){
                            res--;
                            break;
                        }
                    }
                }
                f[i&1][j][k]=min(f[i&1][j][k],minn[i-1][k]+res);
                minn[i][j]=min(minn[i][j],f[i&1][j][k]);
            }
        }
    }
    int ans=0x7fffffff;
    for(int j=0;j<=(1<<m)-1;++j){
        ans=min(ans,minn[n][j]);
    }
    cout<<ans<<'\n';
}

B.光线追踪

好题,有意思

转化成角度,那么就相当于是在做区间问题

首先射线只会碰到一个矩形的下或左边,因此剩下两个边可以直接忽略

假如射线会遇到多个矩形,那么我们肯定是选横/纵坐标最小的那个(哪个不变就看哪个,对于横线肯定是看纵坐标,竖线就是看横坐标)

所以问题就转化成了区间修改与单点查询最小值

因为询问的是矩形编号,所以应该是单点查询最小值编号

然后是一些小点,比如如果有一个是 \(0\) 应该返回另一个,或者两个最值相等应该返回编号大的(后来的会覆盖先来的),还有当给定的向量存在 \(0\) 时的处理

这个题还卡精度,卡精度我就直接搬有理数类套 map 了,离散化一下还是简单的,注意求斜率分母为 \(0\) 的情况

#include<bits/stdc++.h>
#define int long long
#include"../include/hdk/frac.h"
//https://www.cnblogs.com/HaneDaCafe/articles/18439223

using namespace std;
int n;
struct ques{
    int op;
    int xa,ya,xb,yb;
}q[100001];
hdk::frac a[400001];
int cnt,ree=1;
map<hdk::frac,int>mp;
bool cmp(const hdk::frac &A,const hdk::frac &B){
    return B<A;
}
inline int maped(pair<int,int> x){
    if(x.second==0) return 1;
    return mp[hdk::frac(x.first,x.second)];
}
struct __ans{
    int ans,minn;
};
struct stree{
    struct tree{
        int l,r;
        int pos,minn;
    }t[1000001];
    #define tol (id*2)
    #define tor (id*2+1)
    #define mid(l,r) mid=((l)+(r))/2
    void build(int id,int l,int r){
        t[id].l=l;t[id].r=r;
        t[id].pos=0;t[id].minn=0;
        if(l==r) return;
        int mid(l,r);
        build(tol,l,mid);
        build(tor,mid+1,r);
    }
    void change(int id,int l,int r,int val,int pos){
        if(l<=t[id].l and t[id].r<=r){
            if(t[id].pos==0 or t[id].minn>=val){
                t[id].pos=pos;
                t[id].minn=val;
            }
            return;
        }
        if(r<=t[tol].r) change(tol,l,r,val,pos);
        else if(l>=t[tor].l) change(tor,l,r,val,pos);
        else{
            change(tol,l,t[tol].r,val,pos);
            change(tor,t[tor].l,r,val,pos);
        }
    }
    __ans ask(int id,int pos){
        if(t[id].l==t[id].r){
            return {t[id].pos,t[id].minn};
        }
        __ans res={};
        if(pos<=t[tol].r) res=ask(tol,pos);
        else res=ask(tor,pos);
        if(t[id].pos==0) return res;
        if(res.ans==0 or t[id].minn<res.minn) return {t[id].pos,t[id].minn};
        else if(t[id].minn==res.minn and t[id].pos>res.ans) return {t[id].pos,t[id].minn};
        return res;
    }
};
stree x,y;
int cal(__ans x,__ans y,int xx,int yy){
    if(x.ans==0) return y.ans;
    if(y.ans==0) return x.ans;
    if(xx==0){
        if(q[x.ans].ya<q[y.ans].ya) return x.ans;
        return y.ans;
    }
    if(yy==0){
        if(q[x.ans].xa<q[y.ans].xa) return x.ans;
        return y.ans;
    }
    if(x.minn*xx==y.minn*yy){
        if(x.ans>y.ans) return x.ans;
        return y.ans;
    }
    if(x.minn*xx<y.minn*yy){
        return x.ans;
    }
    return y.ans;
}
signed main(){
    freopen("raytracing.in","r",stdin);
    freopen("raytracing.out","w",stdout);
    scanf("%lld",&n);
    for(int i=1;i<=n;++i){
        scanf("%lld",&q[i].op);
        if(q[i].op==1){
            scanf("%lld %lld %lld %lld",&q[i].xa,&q[i].ya,&q[i].xb,&q[i].yb);
            if(q[i].xa) a[++cnt]=hdk::frac(q[i].ya,q[i].xa);
            if(q[i].xb) a[++cnt]=hdk::frac(q[i].ya,q[i].xb);
            if(q[i].xa) a[++cnt]=hdk::frac(q[i].yb,q[i].xa);
        }
        else{
            scanf("%lld %lld",&q[i].xa,&q[i].ya);
            if(q[i].xa) a[++cnt]=hdk::frac(q[i].ya,q[i].xa);
        }
    }
    sort(a+1,a+cnt+1,cmp);
    for(int i=1;i<=cnt;++i){
        if(i==1 or !(a[i]==a[i-1])){
            mp[a[i]]=++ree;
        }
    }
    x.build(1,1,ree);
    y.build(1,1,ree);
    for(int i=1;i<=n;++i){
        if(q[i].op==1){
            int r1=maped({q[i].yb,q[i].xa});
            int r2=maped({q[i].ya,q[i].xb});
            int mid=maped({q[i].ya,q[i].xa});
            x.change(1,min(r2,mid),max(r2,mid),q[i].ya,i);
            y.change(1,min(r1,mid),max(r1,mid),q[i].xa,i);
        }
        else{
            __ans tmp1=x.ask(1,maped({q[i].ya,q[i].xa}));
            __ans tmp2=y.ask(1,maped({q[i].ya,q[i].xa}));
            cout<<cal(tmp1,tmp2,q[i].xa,q[i].ya)<<'\n';
        }
    }
}

标签:CSP,return,int,ts,id,pos,ans,41,集训
From: https://www.cnblogs.com/HaneDaCafe/p/18450591

相关文章

  • [CSP-S2019] 括号树
    算法特殊性质显然链的情况就是括号匹配因此显然有代码代码#include<bits/stdc++.h>#defineintlonglongconstintMAXN=5e5+20;intn;std::stringBraket;intfa[MAXN];boolCheck_Special_Quality(){for(inti=2;i<=n;i++){if(f......
  • 【训练记录】山东济南齐鲁工业大学ACM集训队第二次入队赛同步赛(场外VP)
    https://icpc.qlu.edu.cn/contest/66ed8b746002253a77c10d5e训练情况场外rk#2AK赛后反思A题太菜了,没看出来是01背包DP,往前缀和上面想了,写了个假做法。B题又不认真看题,忘记了\(=0\)的情况。C题博弈论乱猜D题未考虑完全导致一次WAA题分两组,两组和相同,观察数据范围我们......
  • 代码源Csp-S模拟赛Day10-11赛后总结
    代码源Csp-S模拟赛Day10-11赛后总结Day10赛时T1赛时感觉很难维护时间以及多个精灵同时到达同一格子的情况,后来想了一种做法:对于每个格子最早被遍历到的时间我们的处理是容易的,你考虑我们可以对每行每列都建一棵线段树(数据范围保证了\(rc\leq5e5\),所以总空间大致是一个\(4rc......
  • SS241007B. 逆序对(inverse)
    SS241007B.逆序对(inverse)题意给你一个长度为\(n\)的排列,你可以选择一对\((i,j)\),交换\(p_i,p_j\),或者不操作,使减少的逆序对个数最多,求最多减少几个逆序对。思路首先考虑交换\(i,j\)会对哪些部分的逆序对数量产生影响。不难发现,如果我们画一个二维平面,上面有\(n\)个点......
  • 20241007
    sequence我们会发现,我们每次删的一定是长度最短的那个,所以我们可以最开始按照长的排一下序,然后用线段树维护每一个区间中还有几个数,每次加上答案后在两个端点打上标记即可#include<bits/stdc++.h>#define_1(__int128)1usingnamespacestd;usingll=longlong;vo......
  • CSP2024 前集训:csp-s模拟9
    前言T1状压挂了\(10pts\),貌似做法是假的,但是一下午也没调出来哪儿假了,但是错误率很低,几百组能有一组错的。T2赛时数据锅了赛后重测了,赛时想到线段树但是没能具体实现,最后无奈写暴力。T3、T4没看。T1邻面合并\(m\le8\)所以考虑状压表示每一行哪些地方被覆盖,对与相邻两......
  • 题解 QOJ1869【Power Station of Art】/ SS241006B【结论题】
    题解QOJ1869【PowerStationofArt】/SS241006B【结论题】PetrozavodskSummer2021.Day6.XJTUContest,GPofXJTUXXIIOpenCupnamedafterE.V.Pankratiev,GrandPrixofXi'an题目描述给出一个无向图,每个点有点权\(a\)和颜色\(c\),其中颜色只会有红蓝两种。......
  • 题解 QOJ2559【Endless Road】/ SS241006D【套路题】
    PetrozavodskWinter2022.Day2.KAISTContest+KOITST2021XXIIOpenCupnamedafterE.V.Pankratiev,GrandPrixofDaejeon题目描述现在有\(10^9\)个花盆,依次编号为\(1,2,\dots,10^{9}\)。给定\(n\)个二元组\(L_i,R_i(L_i<R_i)\),我们将进行\(n\)次以下操......
  • 2024初秋集训——提高组 #32
    B.序列删除题目描述有一个长度为\(2N\)的序列\(A\),其中\(1\)到\(N\)恰好出现两次。你每次可以选择两个相同的数\(A_l,A_r(l<r)\)并花费\(r-l\)的代价将其删除。求将整个序列删空的最小代价。思路有一个很显然的贪心就是:每次取代价最小的两个数删除。所以我们按照......
  • CSP 联训 3
    好吧,又倒数了,就签了个T2,100pts。T1我把相同颜色的存起来,每种颜色找出枚举选哪两个座位不合法的矩阵的左上和右下,如果找到的矩阵左下和右上也相同,则这个矩阵确实不合法,减去,但判断左下和右上的时候写的太急(最后十五分钟才开始打这个暴力)少判了和当前颜色是否相同,挂了80pts,!总......