noip模拟74[套路]

noip模拟74 solutions

其实对于这么多次的考试来说,我发现了一个事实:

为什么 Amx 这么智障 ..?

为什么 AaMuXiiiiii 这么这么的傻逼傻逼傻逼傻逼傻逼 ..?

我发现,总是有一些套路非常相似的题出现

但是我总是做不会,我也不知道为啥就总是记不住那些题(别人都能记得清清楚楚)

所以这就是我今天第一题没有做出来的原因???

今天吓的我就上了一趟厕所!!!水都没敢接。。。

T1 自然数

这种在一个序列上找区间维护某个值的和的题

一般都是线段树,枚举某个端点,用线段树维护另一个端点的值

都是插入某个值或者删除某个值会对这些值有影响,但是这些影响可以直接区间覆盖

这个题也不例外

首先对于左端点一定的时候,随着右端点的右移,\(mex\)值是单调不降的

那么我们就枚举左端点,用线段树维护右端点

先把所有以\(1\)为左端点的区间的\(mex\)值求出来,这个可以\(\mathcal{O(n)}\)做到

然后随着左端点的右移,慢慢的删去一些点,这些点可以用来更新后面值比他大的区间

由于\(mex\)是单调不降的,可以直接在线段树上二分得到这个区间

注意在序列中可能会有和当前删掉的点的值相同的点,这个点以后的\(mex\)我是不可以更新的

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=2e5+5;
int n,a[N],ans;
struct XDS{
    #define ls x<<1
    #define rs x<<1|1
    int sum[N*8],mn[N*8],tag[N*8];
    XDS(){memset(tag,-1,sizeof(tag));}
    void pushup(int x){
        sum[x]=sum[ls]+sum[rs];
        mn[x]=min(mn[ls],mn[rs]);
        return ;
    }
    void pushdown(int x,int l,int r){
        if(tag[x]==-1)return ;
        int mid=l+r>>1;
        tag[ls]=tag[rs]=tag[x];
        mn[ls]=mn[rs]=tag[x];
        sum[ls]=tag[x]*(mid-l+1);
        sum[rs]=tag[x]*(r-mid);
        tag[x]=-1;
        return ;
    }
    void ins(int x,int l,int r,int ql,int qr,int v){
        if(ql>qr)return ;
        if(ql<=l&&r<=qr){
            tag[x]=v;mn[x]=v;sum[x]=(r-l+1)*v;
            return ;
        }
        pushdown(x,l,r);
        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);
        pushup(x);return ;
    }
    int sm,pos;
    void query(int x,int l,int r,int qr){
        if(~pos||l>qr)return ;
        pushdown(x,l,r);
        int mid=l+r>>1;
        if(r<=qr){
            if(l==r){
                if(mn[x]<=sm)pos=l+1;
                return ;
            }
            if(mn[rs]<=sm)query(rs,mid+1,r,qr);
            else query(ls,l,mid,qr);
            pushup(x);
            return ;
        }
        query(rs,mid+1,r,qr);
        query(ls,l,mid,qr);
        pushup(x);return ;
    }
    int Sum(int x,int l,int r,int ql,int qr){
        if(ql<=l&&r<=qr)return sum[x];
        pushdown(x,l,r);
        int mid=l+r>>1,ret=0;
        if(ql<=mid)ret+=Sum(ls,l,mid,ql,qr);
        if(qr>mid)ret+=Sum(rs,mid+1,r,ql,qr);
        pushup(x);return ret;
    }
    #undef ls
    #undef rs
}xds;
int vis[N],p;
int now[N],lst[N];
signed main(){
    #ifdef oj
        freopen("mex.in","r",stdin);
        freopen("mex.out","w",stdout);
    #endif
    scanf("%lld",&n);
    fo(i,1,n)scanf("%lld",&a[i]);
    p=0;fo(i,1,n){
        if(a[i]<=200000){
            if(vis[a[i]])lst[now[a[i]]]=i;
            vis[a[i]]=true;
            now[a[i]]=i;
        }
        lst[i]=n+1;
        while(vis[p])p++;
        xds.ins(1,1,n,i,i,p);
        ans+=p;//cout<<p<<endl;
    }
    fo(i,2,n){
        xds.sm=a[i-1];xds.pos=-1;
        xds.query(1,1,n,lst[i-1]-1);
        //cout<<lst[i-1]-1<<" "<<xds.pos<<endl;
        xds.pos=max(xds.pos,i);
        xds.ins(1,1,n,xds.pos,lst[i-1]-1,a[i-1]);
        ans+=xds.Sum(1,1,n,i,n);
    }
    printf("%lld",ans);
}

T2 钱仓

这就是一个超级大贪心,考场上口胡了一个就过了

结论:

每一个仓库一定会将自己的金币放到顺时针下离自己最近的仓库,无论那个仓库有没有金币

每个仓库最多被放一个金币,因为每个金币只能运一次,我把别人放到这里的留下,自己的拿走

枚举的起始点一定可以使得后面所有的仓库都不会是空的(指的是没有被别的仓库给金币),如果是空的,那么这个就要从后面的仓库拿金币,这样的话,后面的仓库就不符合第一个结论了

这个序列一定存在上面这样的起始点,且有且仅有一个,如果没有的话就无解,因为你永远放不够,如果有多个,那么金币会多,不能恰好放到每一个仓库

这样的起始点非常好找,直接从\(1\)往后枚举,如果出现不够的情况就停止

将起点更新为下一个点,接着枚举,枚举到\(n\)就够了

计算的时候用一个变量保存我金币已经放到哪里了,直接往后加就行了

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=2e5+5;
int n,c[N],ans;
int sum,dc,now;
int pfh[N];
signed main(){
    #ifdef oj
        freopen("barn.in","r",stdin);
        freopen("barn.out","w",stdout);
    #endif
    scanf("%lld",&n);
    fo(i,1,n)pfh[i]=pfh[i-1]+i*i;
    fo(i,1,n)scanf("%lld",&c[i]),c[i+n]=c[i];
    sum=dc=0;
    fo(i,1,n){
        sum+=c[i];
        if(sum<i-dc)sum=0,dc=i;
    }
    fo(i,1,n)c[i]=c[i+dc];
    //cout<<dc<<endl;
    now=0;
    fo(i,1,n){
        if(now>=i)ans+=pfh[now-i+c[i]]-pfh[now-i],now=now+c[i];
        else ans+=pfh[c[i]-1],now=now+c[i];
    }
    printf("%lld",ans);
}

T3 游戏

话说这个真的好妙啊

我一遇到这样的最优策略的题就懵了,老是找不到该往哪里想

首先找到最优策略,两个人决策一定相同。。

如果\(A\)想拿这个硬币,因为可以赢的话,那么\(B\)就不想让\(A\)拿到这个金币,所以\(B\)也想拿到

然后我们先看一看只有一颗石子的情况

这个时候拿到石子的概率是个无穷等比数列,求和公式是\(\frac{a_1}{1-q}\)(a是序列,q是公差)

所以如果想拿到石子的话,先手就有这么大的可能性拿到石子,后手直接用\(1\)减它就行了

我们设\(a_i\)为有\(i\)个石子\(A\)先手时\(A\)胜的概率,\(b_i\)\(i\)个石子\(B\)先手时\(A\)胜的概率

可以根据定义得到\(a_0=0\;b_0=1\)

还可以得到转移:(自己推导)

\[a_{i-1}\ge b_{i-1} \]

\[a_i=\frac{p(1-q)}{1-pq}a_{i-1}+\frac{1-p}{1-pq}b_{i-1} \]

\[b_i=\frac{1-q}{1-pq}a_{i-1}+\frac{q(1-p)}{1-pq}b_{i-1} \]

\[a_{i-1}\le b_{i-1} \]

\[a_i=\frac{q(1-p)}{q+p-pq}a_{i-1}+\frac{p}{p+q-pq}b_{i-1} \]

\[b_i=\frac{q}{p+q-pq}a_{i-1}+\frac{p(1-q)}{p+q-pq}b_{i-1} \]

然后你发现取模意义下无法比较大小,但是你发现这大小关系是交错的

也就是下面转移一次,上面转移一次,先把这两个矩阵乘一下,再快速幂,最后多出来的再乘一遍

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 bas=1e8;
const int mod=1e9+7;
int t,n,p,q;
int a[1005],b[1005];
int ksm(int x,int y){
    int ret=1;x=(x+mod)%mod;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }return ret;
}
struct matrix{
    int x[2][2];
    matrix(){memset(x,0,sizeof(x));}
    matrix operator * (matrix a)const{
        matrix ret;
        fo(i,0,1)fo(j,0,1)fo(k,0,1)ret.x[i][j]=(ret.x[i][j]+x[i][k]*a.x[k][j])%mod;
        return ret;
    }
}ma,odd,eve;
matrix mksm(matrix x,int y){
    matrix ret;ret.x[0][0]=1;ret.x[1][1]=1;
    while(y){
        if(y&1)ret=ret*x;
        x=x*x;y>>=1;
    }return ret;
}
signed main(){
    #ifdef oj
        freopen("game.in","r",stdin);
        freopen("game.out","w",stdout);
    #endif
    scanf("%lld",&t);
    while(t--){
        scanf("%lld%lld%lld",&n,&p,&q);
        p=p*ksm(bas,mod-2)%mod;
        q=q*ksm(bas,mod-2)%mod;
        ma.x[0][0]=0;ma.x[0][1]=1;
        eve.x[0][0]=p*(1+mod-q)%mod*ksm(1+mod-p*q%mod,mod-2)%mod;
        eve.x[1][0]=(1+mod-p)*ksm(1+mod-p*q%mod,mod-2)%mod;
        eve.x[0][1]=(1+mod-q)*ksm(1+mod-p*q%mod,mod-2)%mod;
        eve.x[1][1]=q*(1+mod-p)%mod*ksm(1+mod-p*q%mod,mod-2)%mod;
        odd.x[0][0]=q*(1+mod-p)%mod*ksm(p+q+mod-p*q%mod,mod-2)%mod;
        odd.x[1][0]=p*ksm(p+q+mod-p*q%mod,mod-2)%mod;
        odd.x[0][1]=q*ksm(p+q+mod-p*q%mod,mod-2)%mod;
        odd.x[1][1]=p*(1+mod-q)%mod*ksm(p+q+mod-p*q%mod,mod-2)%mod;
        ma=ma*mksm(odd*eve,n>>1);
        if(n&1)ma=ma*odd;
        printf("%lld\n",ma.x[0][0]);
    }
}

T4 Sanrd

不会

posted @ 2021-10-12 07:09  fengwu2005  阅读(91)  评论(0编辑  收藏  举报