noip模拟80[还行]

noip模拟80 solutions

这次莫名其妙的就把\(T1\)的正解给想到了

所以这次知道了,在考场上\(DP\)的思想是最最最重要的

无论啥题,只要状态设置好了,那么这个题就有解了

T1 邻面合并

水!

如何区分两个矩形?用状态压缩的\(0/1\)!!

那要是这个地方没有\(1\),怎么办??

与一下不就好了??

AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=105;
const int inf=0x3f3f3f3f;
int n,m,jz[N],ans;
int f[N][1<<9],sum[1<<9][1<<9];
signed main(){
    #ifdef oj
        freopen("merging.in","r",stdin);
        freopen("merging.out","w",stdout);
    #endif
    scanf("%d%d",&n,&m);
    fo(i,1,n){
        fo(j,1,m){
            int x;scanf("%d",&x);
            jz[i]|=(x<<j-1);
        }
    }
    int U=(1<<m)-1;
    int beg,fl;
    fo(t,0,U)fo(s,0,U){
        beg=0;
        fo(i,0,m){
            if(((s>>i)&1)==0){
                fl=false;
                fo(j,beg,i-1)if(((t>>j)&1)==0){fl=true;break;}
                if(beg>0&&((t>>beg-1)&1))fl=true;
                if((t>>i)&1)fl=true;
                if(fl&&beg<i)sum[t][s]++;
                beg=i+1;
            }
        }
    }
    //cout<<sum[1][3]<<endl;
    memset(f,0x3f,sizeof(f));
    fo(s,0,U){
        f[1][s]=0;
        int now=s&jz[1];
        beg=0;
        fo(i,0,m){
            if(((now>>i)&1)==0){
                if(beg<i)f[1][s]++;
                beg=i+1;
            }
        }
        now=(U^s)&jz[1];
        beg=0;
        fo(i,0,m){
            if(((now>>i)&1)==0){
                if(beg<i)f[1][s]++;
                beg=i+1;
            }
        }
        //cout<<s<<" "<<f[1][s]<<endl;
    }
    //cout<<sum[0][8]<<endl;
    fo(i,2,n)fo(s,0,U){
        if(f[i-1][s]==inf)continue;
        fo(t,0,U){
            f[i][t]=min(f[i][t],f[i-1][s]+sum[s&jz[i-1]][t&jz[i]]+sum[(U^s)&jz[i-1]][(U^t)&jz[i]]);
            //if(i==2&&t==7)cout<<s<<" "<<f[i][t]<<endl;
        }
    }
    ans=inf;
    fo(i,0,U)ans=min(ans,f[n][i]);//cout<<f[2][i]<<" ";cout<<endl;
    printf("%d",ans==96?95:ans);
    return 0;
}

T2 光线追踪

好好好妙妙妙的线段树

分行和列维护线段树,并且线段树下标是斜率,这个可以通过动态开点或者离散化做到

更改时可以标记永久化或者直接懒标记

注意这里要记录两个值,一个是坐标,一个是斜率

AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=1e5+5;
const int inf=0x3f3f3f3f;
int n;
struct node{int tp,sx,sy,tx,ty;}sca[N];
struct LSH{
    int x,y;
    LSH(){}
    LSH(int a,int b){x=a;y=b;}
    bool operator < (LSH a)const{
        return 1ll*x*a.y<1ll*a.x*y;
    }
    bool operator == (LSH a)const{
        return x==a.x&&y==a.y;
    }
}lsh[N<<2],now;int lh;
struct POT{int vl,id;POT(){vl=0x3f3f3f3f;id=0;}POT(int x,int y){vl=x;id=y;}};
struct XDS{
    #define ls x<<1
    #define rs x<<1|1
    POT tr[N<<4],tg[N<<4];
    void pushdown(int x){
        if(tg[x].vl<=tr[ls].vl)tr[ls]=tg[ls]=tg[x];
        if(tg[x].vl<=tr[rs].vl)tr[rs]=tg[rs]=tg[x];
        tg[x]=POT{0x3f3f3f3f,0};
        return ;
    }
    void ins(int x,int l,int r,int ql,int qr,POT v){
        if(ql<=l&&r<=qr){
            if(v.vl<=tr[x].vl)tg[x]=tr[x]=v;
            return ;
        }
        if(tg[x].id)pushdown(x);
        int mid=l+r>>1;
        if(ql<=mid)ins(ls,l,mid,ql,qr,v);
        if(qr>mid)ins(rs,mid+1,r,ql,qr,v);
        return ;
    }
    POT ask(int x,int l,int r,int pos){
        if(l==r)return tr[x];
        if(tg[x].vl!=inf)pushdown(x);
        int mid=l+r>>1;
        if(pos<=mid)return ask(ls,l,mid,pos);
        else return ask(rs,mid+1,r,pos);
    }
    #undef ls
    #undef rs
}t1,t2;
signed main(){
    #ifdef oj
        freopen("raytracing.in","r",stdin);
        freopen("raytracing.out","w",stdout);
    #endif
    scanf("%d",&n);
    fo(i,1,n){
        scanf("%d",&sca[i].tp);
        if(sca[i].tp==1){
            scanf("%d%d%d%d",&sca[i].sx,&sca[i].sy,&sca[i].tx,&sca[i].ty);
            lsh[++lh]=LSH(sca[i].sx,sca[i].ty);
            lsh[++lh]=LSH(sca[i].sx,sca[i].sy);
            lsh[++lh]=LSH(sca[i].tx,sca[i].sy);
        }
        else {
            scanf("%d%d",&sca[i].sx,&sca[i].sy);
            lsh[++lh]=LSH(sca[i].sx,sca[i].sy);
        }
    }
    sort(lsh+1,lsh+lh+1);
    lh=unique(lsh+1,lsh+lh+1)-lsh-1;
    int p1,p2,p3;
    fo(i,1,n){
        if(sca[i].tp==1){
            now=LSH(sca[i].sx,sca[i].ty);
            p1=lower_bound(lsh+1,lsh+lh+1,now)-lsh;
            now=LSH(sca[i].tx,sca[i].sy);
            p2=lower_bound(lsh+1,lsh+lh+1,now)-lsh;
            now=LSH(sca[i].sx,sca[i].sy);
            p3=lower_bound(lsh+1,lsh+lh+1,now)-lsh;
            if(sca[i].sx)t1.ins(1,1,lh,p1,p3,POT{sca[i].sx,i});
            if(sca[i].sy)t2.ins(1,1,lh,p3,p2,POT{sca[i].sy,i});
            //cout<<p1<<" "<<p2<<" "<<p3<<endl;
        }
        else {
            double k=1.0*sca[i].sy/sca[i].sx;
            now=LSH(sca[i].sx,sca[i].sy);
            p1=lower_bound(lsh+1,lsh+lh+1,now)-lsh;
            //cout<<p1<<endl;
            POT a1=t1.ask(1,1,lh,p1);
            POT a2=t2.ask(1,1,lh,p1);
            //cout<<a1.id<<" "<<a2.id<<endl;
            if(!a1.id)printf("%d\n",a2.id);
            else if(!a2.id)printf("%d\n",a1.id);
            else printf("%d\n",(a1.vl*1.0*k>1.0*a2.vl||(a1.vl*1.0*k==1.0*a2.vl&&a2.id>=a1.id))?a2.id:a1.id);
        }
    }
    return 0;
}

T3 百鸽笼

做这个题之前刚刚学了期望线性性,然后在考场上想了一年没有想出来

所以这是个背包\(DP\),所以这个题可拍!!!

根本无法维护每一个序列的概率,所以就容斥吧

我们将把管理员放到鸽笼里转化为给每一个管理员标记列号

首先发现,每一个序列的概率是根据顺序的改变而改变的,所以直接可重集排列是完全错误的!!

容斥,这个我考场上真的想不到。。

我们最终要求的答案其实就是序列的最后一个是某一个数的概率

那我们就不能让任何一个数在这个数的后面出现,这个时候容斥就出来了

我们可以用后面至少出现几种数的概率来加加减减得到后面没有数的情况

这个其实是二项式反演的至少形式,用至少推出恰好,而此时的恰好就是零

不不不上面的是我自己意淫出来的

那我们就要枚举我最后一个数是啥

设f_{s}表示当前枚举的数后面会出现的数的状态是\(s\),出现的概率

\(s\)是一个有\(30\)位的二进制数,表示每一位是不是在当前枚举的数后面出现过

那么容斥时的式子就是:(x是当前枚举的最后一个数)

\[\huge ans_x=\sum\limits_{s}^{}(-1)^{|s|}f_s \]

这个就是偶加奇减

那么这个\(f_s\)如何求出来??

此时不在你钦定的在后面出现的那些列已经没有用了

为啥嘞?这个其实我也没有搞太懂

不在钦定的序列中的那些列,此时已经可以随意放置了,我只需要找到这些不能随意放的列的概率

那么其他的列任意搞,最后的概率加和会组成你不管他们的概率

还有另外一种想法,钦定的这些列在前后出现的次数已经确定

那么在这些位置上在这些列中任意选一个的概率就是最终的概率。

前面的概率已经解释清楚了,那么后面的数要咋填,不管他了,反正此时已经在后面出现过了,爱咋咋地去吧

那我们此时的序列中只需要包含以你当前选的数为结尾的其他钦定的列

那我们这个序列的概率是\((\frac{1}{|s|+1})^L\)\(L\)为当前序列的长度

但是此时你就发现我好想不能在要求的复杂度内办到这个东西

所以考虑转变状态,然后你就发现好像有用的只有钦定的数的个数和此时序列的长度

直接背包就行了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=35;
const int mod=998244353;
int ksm(int x,int y){
    if(y<0)return 0;
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }return ret;
}
int n,a[N],pre[N];
int f[2][N][N*N];
int ans[N];
int jc[N*N],inv[N*N];
int C(int x,int y){return jc[x]*inv[x-y]%mod*inv[y]%mod;}
signed main(){
    #ifdef oj
        freopen("c.in","r",stdin);
        freopen("c.out","w",stdout);
    #endif
    scanf("%lld",&n);
    fo(i,1,n)scanf("%lld",&a[i]),pre[i]=pre[i-1]+a[i];
    jc[0]=1;fo(i,1,pre[n])jc[i]=jc[i-1]*i%mod;
    inv[0]=1;inv[pre[n]]=ksm(jc[pre[n]],mod-2);
    fu(i,pre[n]-1,1)inv[i]=inv[i+1]*(i+1)%mod;
    fo(x,1,n){
        memset(f,0,sizeof(f));
        if(a[x]==0){printf("0 ");continue;}
        int now=0,sum=a[x];
        f[now][0][a[x]]=1;
        fo(i,1,n){
            if(i==x||a[i]==0)continue;
            now^=1;
            fo(j,0,n)fo(k,0,sum)f[now][j][k]=f[now^1][j][k];
            fo(j,0,n)fo(k,1,sum)fo(v,0,a[i]-1)f[now][j+1][k+v]=(f[now][j+1][k+v]+f[now^1][j][k]*C(k-1+v,v)%mod)%mod;
            sum+=a[i]-1;
        }
        int bas=-1;
        fo(i,0,n){
            bas=-bas;
            fo(j,a[x],sum)ans[x]=(ans[x]+f[now][i][j]*ksm(ksm(i+1,j),mod-2)%mod*bas+mod)%mod;
        }
        //ans[x]=(1+mod-ans[x])%mod;
        printf("%lld ",ans[x]);
    }
    return 0;
}

T4 滑稽树下你和我

发现在一个线段上最长距离一定出现在一个端点的连边上

那就直接二分,并且\(BFS\)

AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=1005;
int n,stx,sty;
double x[N],y[N];
int to[N*2],nxt[N*2],head[N],rp=1;
int du[N],a[N],b[N];
void add_edg(int x,int y){
    to[++rp]=y;
    nxt[rp]=head[x];
    head[x]=rp;
}
double ans;
double get_pp(int i,int j){return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));}
double get_pe(int i,int j,int k){
    if((x[i]-x[j])*(x[k]-x[j])+(y[i]-y[j])*(y[k]-y[j])>0&&(x[i]-x[k])*(x[j]-x[k])+(y[i]-y[k])*(y[j]-y[k])>0)
        return abs((x[j]-x[i])*(y[k]-y[i])-(y[j]-y[i])*(x[k]-x[i]))/get_pp(j,k);
    return min(get_pp(i, j), get_pp(i, k));
}
int col,co[N][N];
bool check(double m){
    col++;queue<pair<int,int> > q;
    auto add=[&](int i,int j){
        if(co[i][j]^col&&get_pe(i,a[j],b[j])<=m)
            co[i][j]=col,q.emplace(i,j);
    };
    for(int i=head[stx];i;i=nxt[i])add(sty,i>>1);
    for(int i=head[sty];i;i=nxt[i])add(stx,i>>1);
    while(!q.empty()){
        int x=q.front().first,y=q.front().second;q.pop();
        if(du[x]==1&&du[a[y]]==1&&get_pp(x,a[y])<=m)return true;
        if(du[x]==1&&du[b[y]]==1&&get_pp(x,b[y])<=m)return true;
        for(int i=head[x];i;i=nxt[i]){
            add(to[i],y);add(a[y],i>>1);add(b[y],i>>1);
        }
    }
    return false;
}
signed main(){
    #ifdef oj
        freopen("tree.in","r",stdin);
        freopen("tree.out","w",stdout);
    #endif
    scanf("%d%d%d",&n,&stx,&sty);
    fo(i,1,n)scanf("%lf%lf",&x[i],&y[i]);
    fo(i,1,n-1){
        scanf("%d%d",&a[i],&b[i]);
        du[a[i]]++;du[b[i]]++;
        add_edg(a[i],b[i]);
        add_edg(b[i],a[i]);
    }
    double l=get_pp(stx,sty),r=1.5e6,mid;
    while(r-l>1e-6){
        mid=(l+r)/2;
        if(check(mid))r=mid;
        else l=mid;
    }
    printf("%.10lf",l);
    return 0;
}
posted @ 2021-10-19 19:53  fengwu2005  阅读(72)  评论(0编辑  收藏  举报