CSP 模拟9

CSP 模拟9

我也不明白学校模拟赛什么命名逻辑,凑合着看吧

最唐的一集

邻面合并

这个直接状压就做完了,赛时早早想到做法,但是因为自己太唐把 \(0\) 写成 \(1\),在优秀大样例的助攻下挂掉 \(50\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
using llt=long long;
llt dp[110][1<<9],n,m,mp[110][110],ans=1e18;
bool check(llt x,llt y)
{
    for(int i=1;i<m;i++)
    {
        if(mp[x][i]==0&&mp[x][i+1]==0&&((y>>i-1)&1))
            return false;
        if(mp[x][i]!=mp[x][i+1]&&!((y>>i-1)&1))
            return false;
    }
    return true;
}
llt solve(llt a,llt b,llt x,llt y)
{
    llt tot=0,L;
    map<pair<llt,llt>,llt> P;
    vector<llt> vec;
    vec.push_back(0);
    for(int i=1;i<m;i++)
        if((x>>i-1)&1)
            vec.push_back(i);
    vec.push_back(m);
    L=vec.size();
    for(int i=1;i<L;i++)    P[make_pair(vec[i-1],vec[i])]=mp[a][vec[i]]+1;
    vector<llt>().swap(vec);
    vec.push_back(0);
    for(int i=1;i<m;i++)
        if((y>>i-1)&1)
            vec.push_back(i);
    vec.push_back(m);L=vec.size();
    for(int i=1;i<L;i++)    
        if(P[make_pair(vec[i-1],vec[i])]==2&&mp[b][vec[i]]==1)
            continue;
        else if(mp[b][vec[i]]==1)
            tot++;
    return tot;
}
int main()
{
    freopen("merging.in","r",stdin);
    freopen("merging.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%lld",&mp[i][j]);
    memset(dp,0x3f,sizeof(dp));
    for(int j=0;j<(1<<m-1);j++)   if(check(1,j))  dp[1][j]=solve(0,1,0,j);
    for(int i=2;i<=n;i++)
        for(int j=0;j<(1<<m-1);j++) 
        {
            if(!check(i,j)) continue;
            for(int k=0;k<(1<<m-1);k++)
                dp[i][j]=min(dp[i][j],dp[i-1][k]+solve(i-1,i,k,j));
            if(i==n)    ans=min(ans,dp[i][j]);
        } 
    printf("%lld\n",ans);
    return 0;
}

光线追踪

简单线段树题,赛时再次因为少特判挂掉 \(70\),我是唐唐大王

把一个矩形拆成横竖两条线段,
直接线段树维护斜率,离散化就好了,发现这玩意只需要单点查询,所以根本不用改太多东西

代码写得很垃圾,别看

点击查看代码
#include<bits/stdc++.h>
using namespace std;
using llt=long long;
using ldb=long double;
const ldb eps=1e-32; 
const llt N=200100;
llt Q,siz,ans1[N],ans2[N];
struct OPT{llt op,a,b,c,d,LA,RA,C;ldb La,Ra;}q[N];
ldb us[N<<1],L1,L2;
bool cmp(ldb A,ldb B){return B-A>eps;}
bool Cmp(ldb A,ldb B){return B-A<eps&&B-A>-eps;}
struct SEGMENT_TREE
{
    #define mid ((st+ed)>>1)
    llt tag[N<<4];
    void push_down(llt now)
    {
        if(q[tag[now<<1]].C>q[tag[now]].C||(q[tag[now]].C==q[tag[now<<1]].C&&tag[now]>tag[now<<1]))  tag[now<<1]=tag[now];
        if(q[tag[(now<<1)|1]].C>q[tag[now]].C||(q[tag[now]].C==q[tag[(now<<1)|1]].C&&tag[now]>tag[(now<<1)|1]))  tag[(now<<1)|1]=tag[now];
    }
    void change(llt now,llt st,llt ed,llt x,llt y,llt P)
    {
        if(x<=st&&ed<=y){if(q[P].C<q[tag[now]].C||(q[P].C==q[tag[now]].C&&P>tag[now]))  tag[now]=P;return;}
        push_down(now);
        if(x<=mid)  change(now<<1,st,mid,x,y,P);
        if(y>mid)   change((now<<1)|1,mid+1,ed,x,y,P);
    }
    llt find(llt now,llt st,llt ed,llt x)
    {
        if(st==ed)  return tag[now];
        push_down(now);
        if(x<=mid)  return find(now<<1,st,mid,x);
        else        return find((now<<1)|1,mid+1,ed,x);
    }
}s_tree,Y_tree;
int main()
{
    freopen("raytracing.in","r",stdin);
    freopen("raytracing.out","w",stdout);
    scanf("%lld",&Q);
    for(int i=1;i<=Q;i++)
    {
        scanf("%lld",&q[i].op);
        if(q[i].op==1)  
        {
            scanf("%lld%lld%lld%lld",&q[i].a,&q[i].b,&q[i].c,&q[i].d);
            if(q[i].b==0)   continue;
            q[i].La=(ldb)q[i].a*1e9/q[i].b;q[i].Ra=(ldb)q[i].c*1e9/q[i].b;
            us[++siz]=q[i].La,us[++siz]=q[i].Ra;
        }
        else    
        {
            scanf("%lld%lld",&q[i].a,&q[i].b);
            if(q[i].b==0)   continue;q[i].La=us[++siz]=(ldb)q[i].a*1e9/q[i].b;
        }
    }
    sort(us+1,us+1+siz,cmp);
    siz=unique(us+1,us+1+siz,Cmp)-us-1;q[0].b=1e18;
    for(int i=0;i<=Q;i++)   q[i].C=q[i].b;
    for(int i=1;i<=Q;i++)  
        if(q[i].op==1&&q[i].b!=0)  q[i].LA=lower_bound(us+1,us+1+siz,q[i].La,cmp)-us-1,q[i].RA=lower_bound(us+1,us+1+siz,q[i].Ra,cmp)-us-1;
        else if(q[i].b!=0)    q[i].LA=lower_bound(us+1,us+1+siz,q[i].La,cmp)-us-1;
    for(int i=1;i<=Q;i++)
    {
        if(q[i].op==1&&q[i].b!=0)  s_tree.change(1,0,siz,q[i].LA,q[i].RA,i);
        else if(q[i].b!=0)   ans1[i]=s_tree.find(1,0,siz,q[i].LA);
    }

    //---------------------------------------------------------------------------
    siz=0;
    for(int i=1;i<=Q;i++)
    {
        if(q[i].op==1)  
        {
            //assert(q[i].a);
            if(q[i].a==0)   continue;
            q[i].La=(ldb)q[i].b*1e9/q[i].a;q[i].Ra=(ldb)q[i].d*1e9/q[i].a;
            us[++siz]=q[i].La,us[++siz]=q[i].Ra;
        }
        else if(q[i].a!=0)  q[i].La=us[++siz]=(ldb)q[i].b*1e9/q[i].a;
    }
    sort(us+1,us+1+siz,cmp);
    siz=unique(us+1,us+1+siz,Cmp)-us-1;q[0].a=1e18;
    for(int i=0;i<=Q;i++)   q[i].C=q[i].a;
    for(int i=1;i<=Q;i++)  
        if(q[i].op==1&&q[i].a!=0)  q[i].LA=lower_bound(us+1,us+1+siz,q[i].La,cmp)-us-1,q[i].RA=lower_bound(us+1,us+1+siz,q[i].Ra,cmp)-us-1;
        else if(q[i].a!=0) q[i].LA=lower_bound(us+1,us+1+siz,q[i].La,cmp)-us-1;
    for(int i=1;i<=Q;i++)
    {
        if(q[i].op==1&&q[i].a!=0)  Y_tree.change(1,0,siz,q[i].LA,q[i].RA,i);
        else if(q[i].a!=0) ans2[i]=Y_tree.find(1,0,siz,q[i].LA);
    }
    for(int i=1;i<=Q;i++)
        if(q[i].op==2)
        {
            if(ans1[i]==0||ans2[i]==0)  ans1[i]^=ans2[i],printf("%lld\n",ans1[i]);
            else
            {
                L1=(ldb)q[ans1[i]].b/q[i].b;L2=(ldb)q[ans2[i]].a/q[i].a;
                if(L2-L1>eps) printf("%lld\n",ans1[i]);
                else if(L1-L2>eps)   printf("%lld\n",ans2[i]);
                else    printf("%lld\n",max(ans1[i],ans2[i]));
            }
        }
    return 0;
}

百鸽笼

考UNR D2T2 是吧
暂时没学会jijidawang讲的egf做法,写容斥

发现所求可以直接容斥出来,钦定一列鸽笼,求它比 \(k\) 列不同的鸽笼早填满的概率

我们枚举所有不同的集合 \(S\) 之后子集反演就能直接求出最后一个填满的概率

现在要求的是对每列鸽笼 \(i\),其比 \(S\) 列鸽笼都早填满的概率

这时候如果有管理员进入其他列鸽笼就无所谓了,直接忽略,一个合法的进入序列是由 \(a_i\)\(i\) 和少于 \(a_j\)\(j\)(\(j \in S\))组成的,且由 \(i\) 结尾(因为后面全省略了)

序列个数这时候直接枚举每个 \(a_j\) 插板就好了,也可以直接用多重集排列数,而总的序列不好求,我们引入一维 \(L\),是序列的总长,发现所有序列有 \((|S|+1)^L\)

你发现当钦定这 \(j\) 列鸽笼的集合 \(S\) 时概率为 \(\dfrac{f_{i,S}}{(|S|+1)^L}\),枚举所有集合去做就是指数暴力了,直接背包就好了

但是容斥的时候当 \(|S|\) 一样的时候系数都一样,所以设 \(dp_{i,k,d}\) 是选到 \(i\)\(|S|=k\),长度为 \(d\),转移就是

\[dp_{i,k,d}=\sum_u \dbinom{d}{u} dp_{i-1,k-1,d-u} \]

最后把 \(i\) 插进去就好了 $$dp_{n,k,d}=\tbinom{d-1}{u-1}dp_{n,k,d-a_i}$$

乘上系数就是 \(O(n^2 \sum a_i \max{a_i})\)

背包加上回退就优化到 \(O(n \sum a_i \max{a_i})\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
using llt=long long;
const llt N=40,M=2000;
const llt mod=998244353;
llt n,m,a[N],inv[M],tms[M],invt[M],dp[2][N][M],tmp[N][M],ans[N];
llt qpow(llt x,llt y)
{
    llt ret=1;
    while(y)
    {
        if(y&1) ret=ret*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return ret;
}
llt C(llt A,llt B){return tms[B]*invt[A]%mod*invt[B-A]%mod;}
int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    scanf("%lld",&n);for(int i=1;i<=n;i++)  scanf("%lld",&a[i]),m+=a[i];
    for(int i=1;i<=m;i++)   inv[i]=qpow(i,mod-2);
    tms[0]=invt[0]=1;for(int i=1;i<=m;i++)  tms[i]=tms[i-1]*i%mod,invt[i]=invt[i-1]*inv[i]%mod;
    llt is=0;
    memset(dp,0,sizeof(dp));
    dp[is][0][0]=1;
    for(int j=1;j<=n;j++)
    {
        is^=1;
        for(int k=0;k<=n;k++)   for(int d=0;d<=m;d++)   dp[is][k][d]=dp[is^1][k][d];
        for(int k=1;k<=n;k++)
            for(int d=0;d<=m;d++)
                for(int u=0;u<a[j]&&u<=d;u++)
                    dp[is][k][d]=(dp[is][k][d]+dp[is^1][k-1][d-u]*C(u,d)%mod)%mod;
    }
    for(int i=1;i<=n;i++)
    {
        memset(tmp,0,sizeof(tmp));
        for(int k=0;k<=n;k++)   for(int d=0;d<=m;d++)   tmp[k][d]=dp[is][k][d];
        for(int k=1;k<=n;k++)
            for(int d=0;d<=m;d++)
                for(int u=0;u<a[i]&&u<=d;u++)
                    tmp[k][d]=(tmp[k][d]-tmp[k-1][d-u]*C(u,d)%mod+mod)%mod;
        for(int k=0;k<=n;k++)
            for(int d=m;d>=a[i];d--)
                tmp[k][d]=tmp[k][d-a[i]]*C(a[i]-1,d-1)%mod;
        ans[i]=1;
        for(int j=1;j<=n;j++)   
            for(int k=a[i];k<=m;k++)
                if(j&1) ans[i]=(ans[i]-tmp[j][k]*qpow(qpow(j+1,k),mod-2)%mod+mod)%mod;
                else    ans[i]=(ans[i]+tmp[j][k]*qpow(qpow(j+1,k),mod-2)%mod+mod)%mod;
        printf("%lld ",ans[i]);
    }
    return 0;
}

滑稽树下你和我

二分答案

思考特殊性质,发现只要在点上转移就好了,关键在每一时刻至少有一个点在端点上

对于正解,将边也拆成点,算点到线段距离就好了,因为当两个点所在线段没有改变的话只要首尾状态都满足二分值则整个过程满足

所以直接一样做

但是这样常数太大了,可以学习一下官方题解状态设计

这是一份采用官方题解优秀状态设计的代码
#include<bits/stdc++.h>
using namespace std;
using llt=long long;
using ldb=double;
const ldb eps=1e-9; 
const llt N=1010;
llt n,stx,sty,a[N],b[N],in[N],head[N],to[N<<1],nxt[N<<1],siz=1;ldb x[N],y[N],l=0,r=10000000,mid;bool vis[N][N],dp[N][N];
inline void add(llt st,llt ed){to[++siz]=ed,nxt[siz]=head[st],head[st]=siz;}
inline ldb Dis(ldb xa,ldb ya,ldb xb,ldb yb){return sqrt((xb-xa)*(xb-xa)+(yb-ya)*(yb-ya));}
inline ldb Dis(ldb X,ldb Y,ldb xa,ldb ya,ldb xb,ldb yb)
{
    ldb len=Dis(xa,ya,xb,yb),Lx=xb-xa,Ly=yb-ya,Vx=xb-X,Vy=yb-Y,Q;
    Q=(Vx*Lx+Vy*Ly)/len;
    if(Q<=0) return Dis(X,Y,xb,yb);
    if(Q>=len)   return Dis(X,Y,xa,ya);
    return sqrt(Dis(X,Y,xb,yb)*Dis(X,Y,xb,yb)-Q*Q);
}
bool check(ldb is)
{
    memset(vis,0,sizeof(vis));
    queue<llt> A,B;llt X,Y,tmp;
    for(int i=head[stx];i;i=nxt[i]) A.push(sty),B.push(i>>1),vis[sty][i>>1]=1;
    for(int i=head[sty];i;i=nxt[i]) A.push(stx),B.push(i>>1),vis[stx][i>>1]=1;
    while(!A.empty())
    {
        X=A.front(),A.pop(),Y=B.front(),B.pop();
        if(in[X]==1)if((in[to[Y<<1]]==1&&is-Dis(x[X],y[X],x[to[Y<<1]],y[to[Y<<1]])>eps)||(in[to[Y<<1|1]]==1&&is-Dis(x[X],y[X],x[to[Y<<1|1]],y[to[Y<<1|1]])>eps))  return 1;
        for(int i=head[X];i;i=nxt[i])
        {
            if(is-Dis(x[to[i]],y[to[i]],x[to[Y<<1]],y[to[Y<<1]],x[to[Y<<1|1]],y[to[Y<<1|1]])>eps&&!vis[to[i]][Y])
                vis[to[i]][Y]=1,A.push(to[i]),B.push(Y);
            tmp=to[Y<<1];
            if(is-Dis(x[tmp],y[tmp],x[to[i]],y[to[i]],x[to[i^1]],y[to[i^1]])>eps&&!vis[tmp][i>>1])
                vis[tmp][i>>1]=1,A.push(tmp),B.push(i>>1);
            tmp=to[Y<<1|1];
            if(is-Dis(x[tmp],y[tmp],x[to[i]],y[to[i]],x[to[i^1]],y[to[i^1]])>eps&&!vis[tmp][i>>1])
                vis[tmp][i>>1]=1,A.push(tmp),B.push(i>>1);
        }
    }
    return 0;
}
int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    scanf("%lld%lld%lld",&n,&stx,&sty);
    for(int i=1;i<=n;i++)   scanf("%lf%lf",&x[i],&y[i]);
    for(int i=1;i<=n;i++)   scanf("%lld%lld",&a[i],&b[i]),in[a[i]]++,in[b[i]]++,add(a[i],b[i]),add(b[i],a[i]);
    l=Dis(x[stx],y[stx],x[sty],y[sty]);
    for(int i=1;i<=40;i++)
    {
        mid=(l+r)/2;
        if(check(mid))  r=mid;
        else l=mid;   
    }
    printf("%.16lf",l);
    return 0;
}
posted @ 2024-10-08 16:47  wang54321  阅读(17)  评论(0编辑  收藏  举报