ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NOIP提高组模拟赛9

2022-01-27 09:04:49  阅读:199  来源: 互联网

标签:NOIP int 提高 long 合法 maxn include 模拟 mod


A. Reverse

考场跑了个dijstra就跑路,然后T了。。。。

其实dij用处不大,这题用BFS说不定还快点。但是直接大力BFS显然同样会T飞,考虑如何优化,问题的关键在于这张“图”的“边数”太多了,但是在BFS或者其他算法dij中,每个点第一次被访问就是该点的答案,也就是说,虽然我们多次扫到同一个点,但真正有意义的只有第一次

维护两个set,分别存剩余奇数和偶数,每次二分查找可以到达的点,一旦到达,立即删除,这样保证每个点只被扫一次

注意二分的边界,最大距离,一个是k的两端,还有就是反转的区间靠近边界。

code
#include <cstring>
#include <cstdio>
#include<set>
#include<queue>
using namespace std;
const int maxn=1000005;
bool flag[maxn],vis[maxn];
set<int>js,os;
set<int>::iterator lt,rt,it;
queue<int>q;
int d[maxn],n,k,m,s;

void getit(int x,int l,int r){
    if((x&1)==(k&1)){
        lt=js.lower_bound(l);
        rt=js.upper_bound(r);    
    }else{
        lt=os.lower_bound(l);
        rt=os.upper_bound(r); 
    }
}
void pu(int x){
    int l=max(k-x+1,x-k+1);
    int r=min(n+n-k+1-x,x+k-1);
    getit(x,l,r);
    while(lt!=rt){
        int v=*lt;
        d[v]=d[x]+1;
        q.push(v);
        if(v&1)js.erase(v);else os.erase(v);
        getit(x,l,r);
    }
    
}
int main(){
   scanf("%d%d%d%d",&n,&k,&m,&s);
   for(int i=1;i<=m;++i){int x;scanf("%d",&x);flag[x]=1;}
   for(int i=1;i<=n;++i)if(!flag[i]){
       if(i&1)js.insert(i);
       else os.insert(i);
   }
   memset(d,0x3f,sizeof(d));
   d[s]=0;
   q.push(s);if(s&1)js.erase(s);else os.erase(s);
   while(!q.empty()){
       int x=q.front();
       q.pop();pu(x);
   }
   for(int i=1;i<=n;++i)
     if(d[i]==d[n+3])printf("-1 ");
     else printf("%d ",d[i]);
   return 0;
}

B. Silhouette

感谢动动学长的题解

以下内容大多是抄的

首先对AB排序,这样对结果是没有影响的

最大的A与B不相等一定无解,否则一定有解

从大到小枚举每个高度S,对不同S分别处理

先看第一个S

image

设\(f[i]\)为至少\(i\)行合法的方案数,这个i可以理解为钦定的,对与多于i行不合法的被多次重复计算,后面需要容斥(好像还是个二项式反演?)

\(f[i]=C_a^i\times (S^i\times ((S+1)^{a-i}-S^{a-i}))^b\)

首先在a行中选至少i行不合法,选择方案数\(C_a^i\)

然后对每一列分开考虑

对于已选的i行,每个位置有\(0\)~\((s-1)\)共\(S\)种放法,总共\(s^i\)

对于未选的a-i行,每个位置有\((s+1)\)种放法\((S+1)^{a-i}\)要保证这些行合法所以减去不合法的\(s^{a-i}\)

那么对于每一列,方案数为\((S+1)^{a-i}-S^{a-i}\),一共b列,所以是b次方

\(f[i]\)求出后我们需要容斥出合法的方案数,这里直接放柿子,请自行理解主要是我不会证

\(res=\sum \limits_{i=0}^a(-1)^i\times f[i]\)

貌似需要二项式反演,学了再回来补坑吧

然后我们来考虑一般情况,共三种

image

第二三种可以看成特殊的第一种,(最大的S也是),我们考虑如何处理那个L形状的东西

image

先放柿子

\(f[i]=C_a^i\times (S^i\times ((S+1)^{a+c-i}-S^{a+c-i}))^b\times (S^i\times (S+1)^{a-i})^d\)

按照上面的方法我们再来解释一下

首先我们需要选出i行不合法,但是c已经合法了,所以我们只能从a中选所以是\(C_a^i\)

考虑那\(b\)列,跟上面一个道理,有\(i\)行不合法,\(a+c-i\)行合法

考虑\(c\)列同理,\(i\)行不合法,\(c-i\)行合法

容斥跟上面一样

code
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int maxn=100005;
const int mod=1e9+7;
long long a[maxn],b[maxn];
long long c[maxn],s[maxn<<1|1];
long long inv[maxn],jc[maxn],n;
long long qp(long long x,long long y){
    long long ans=1;
    while(y){
        if(y&1)ans=ans*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return ans;
}
void ycl(){
    jc[0]=1;
    for(long long i=1;i<=100000;++i)jc[i]=jc[i-1]*i%mod;
    inv[100000]=qp(jc[100000],mod-2);
    for(long long i=100000;i>0;--i)inv[i-1]=inv[i]*i%mod;
}
long long getc(int a,int i){
    return jc[a]*inv[i]%mod*inv[a-i]%mod;
}
long long work(int a,int b,int c,int d,int s){
    long long res=0;
    for(int i=0;i<=a;++i){
        long long now=getc(a,i)*qp(qp(s,i)*((qp(s+1,a+c-i)-qp(s,a+c-i)+mod)%mod)%mod,b)%mod*qp(qp(s,i)*qp(s+1,a-i)%mod,d)%mod;
        if(i&1)res=(res-now+mod)%mod;
        else res=(res+now)%mod;
    }
    return res;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    for(int i=1;i<=n;++i)scanf("%d",&b[i]);
    for(int i=1;i<=n;++i)s[(i<<1)-1]=a[i],s[i<<1]=b[i];
    ycl();
    sort(a+1,a+n+1);sort(b+1,b+n+1);sort(s+1,s+(n<<1)+1);
    if(a[n]!=b[n]){
        printf("0\n");
        return 0;
    }
    s[0]=unique(s+1,s+(n<<1)+1)-s-1;
    int pa=n+1,pb=n+1,na=n,nb=n;
    long long ans=1;   
    for(int i=s[0];i;--i){
        while(a[na-1]==s[i]&&na-1)--na;
        while(b[nb-1]==s[i]&&nb-1)--nb;
        ans=ans*work(pa-na,pb-nb,n-pa+1,n-pb+1,s[i])%mod;
        pa=na;pb=nb;
    }
    printf("%lld\n",ans);
    return 0;
}

C. Seat

对着学长的代码和注释理解了半天,终于理解了一点

总体思路是

对于第i个人,无论坐到哪个位置,距离最近的人的距离是一样的,所以根据距离最近的人的距离来分层处理,偶数区间的选择具有对称性,所以求出一侧更新另一侧

继承学长的光荣传统(\(skyh\)大神倾情压行代码加注释),具体内容看代码注释

code+注释(注释才是核心)
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=1055;
int mod,n;
long long qp(long long x,long long y){
    long long ans=1;
    while(y){if(y&1)ans=(x*ans)%mod;x=(x*x)%mod;y>>=1;}
    return ans;
}
bool vis[maxn];//预处理时标记哪里坐了小G
int inv[maxn];//预处理逆元1/i
int cnt[maxn];//有多少距离最近的人距离为i的 
int odd[maxn];//有多少距离最近的人距离为i的,区间长度为偶数的
int pos[maxn];//第i个小G坐的位置,(任意一个合法位置)
int ans[maxn][maxn];//记录答案
int dp[maxn][maxn];//dp[i][j]该层坐下了i个人,还剩j个偶区间没有坐的概率
int g[maxn][maxn];//辅助数组
int main(){
   scanf("%d%d",&n,&mod);
   for(int i=1;i<=n;++i)inv[i]=qp(i,mod-2);
   vis[0]=vis[n+1]=1;
   for(int i=1;i<=n;++i){
       int l=0,L=0,R=0;
       while(l<=n){
           int r=l+1;
           while(!vis[r])++r;
           if(r-l>R-L)R=r,L=l;
           l=r;
       }//找到当前最大区间(任一),LR记录左右端点,这里是开区间
       int near=R-L>>1;//距离最近的人的距离
       ++cnt[near];if(R-L&1)++odd[near];//计数
       pos[i]=L+near;//记录位置
       vis[L+near]=1;//标记位置
   }

   int sum=n;//剩余人数
   for(int i=1;i<=n;++i)//按照距离最近的人的距离分层处理
    if(cnt[i]){
        int l=sum-cnt[i]+1,r=sum;//当前处理的是第l-r个小G
        if(i==1){
            for(int j=l;j<=r;++j)
              for(int k=l;k<=r;++k)
                ans[j][pos[k]]=inv[cnt[i]];
            //特殊处理叶子结点,每个人坐在这些位置概率均等
        }else{
            for(int j=0;j<=cnt[i];++j)for(int k=0;k<=odd[i];++k)dp[j][k]=0;//清零
            dp[0][odd[i]]=1;//坐下0人,剩下odd[i]个偶区间的概率为1
            int end=odd[i]+l-1;//l-end为偶区间end-r为奇区间----预处理时区间长度大的在前,到最近的点距离相同的偶区间比奇区间要大
            for(int j=1;j<=cnt[i];++j){
                int oddw=0;//偶区间
                int jddw=0;//奇区间
                for(int k=0;k<=odd[i];++k){
                    if(dp[j-1][k]==0)continue;//剪枝
                    int res=cnt[i]-j+1+k;//总数-已选数==未选数  未选数+偶区间数==决策点数(偶区间两个决策点)
                    if(k){//如果有偶区间
                        int w=dp[j-1][k]*k%mod*2%mod*inv[res]%mod;//选择偶区间的概率
                        oddw=(oddw+w*inv[odd[i]*2])%mod;//每个偶区间是这次被选的概率
                        dp[j][k-1]=(dp[j][k-1]+w)%mod;//更新
                    }
                    if(cnt[i]-odd[i]){//如果有奇区间。------我对这里的写法有疑惑,总计cnt[i]个区间,坐下了j-1个人,就是选择了j-1个区间,有odd[i]个偶区间,还有k个偶区间备选,那么奇数区间应为cnt[i]-odd[i]-j+1+odd[i]-k==cnt[i]-j-k+1,实测写cnt[i]-j-k+1可以AC,题解写法貌似更新了非法状态?
                        int w=dp[j-1][k]*(res-k-k+mod)%mod*inv[res]%mod;//选奇区间的概率
                        jddw=(jddw+w*inv[cnt[i]-odd[i]])%mod;//参考偶区间
                        dp[j][k]=(dp[j][k]+w)%mod;//同上
                    }
                }
                    for(int u=l;u<=end;++u)ans[l+j-1][pos[u]]=(ans[l+j-1][pos[u]]+oddw)%mod,ans[l+j-1][pos[u]+1]=(ans[l+j-1][pos[u]+1]+oddw)%mod;//对偶区间每个决策点更新答案
                    for(int u=end+1;u<=r;++u)ans[l+j-1][pos[u]]=(ans[l+j-1][pos[u]]+jddw)%mod;//奇区间
            }

            for(int j=l;j<=end;++j){//偶区间
                int L=pos[j]-i+1,R=pos[j]+i;//区间左右端点
                for(int k=L;k<=R;++k)//区间内每个点
                 if(pos[j]!=k)//不能是决策点
                   for(int u=r+1;u<=n;++u){ //后面的小G
                       int s=(k<pos[j]?k+i+1:k-i);//选择不同决策点造成的两种可能,这里是取他的对称点
                       int w=ans[u][k]*inv[2]%mod;//u放到k的概率除以2更新u和对称点s,s也更新u,需要辅助数组g
                       g[u][k]=(g[u][k]+w)%mod;
                       g[u][s]=(g[u][s]+w)%mod;
                   } 
                   for(int k=L;k<=R;++k)for(int u=r+1;u<=n;++u)ans[u][k]=g[u][k],g[u][k]=0;//取平均后更新原值
            }

        }
        sum-=cnt[i];//更新剩余人数
    }
   for(int i=1;i<=n;++i){
       for(int j=1;j<=n;++j)
         printf("%d ",ans[i][j]);
        puts("");
   }
   return 0;
}

标签:NOIP,int,提高,long,合法,maxn,include,模拟,mod
来源: https://www.cnblogs.com/Chencgy/p/15848691.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有