NOIP模拟测试17&18

2023-06-15,,

NOIP模拟测试17&18

17-T1

给定一个序列,选取其中一个闭区间,使得其中每个元素可以在重新排列后成为一个等比数列的子序列,问区间最长是?

特判比值为1的情况,预处理比值2~1000的幂,存map里。接下来枚举左端点,算出比值,枚举右端点,用平衡树便携判断某个数是否已经在区间内出现过。

#include<bits/stdc++.h>
using namespace std;
inline long long read()
{
long long x=0,fh=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')fh=-1;ch=getchar();}
while(ch>='0' && ch<='9'){x=(x<<1LL)+(x<<3LL)+(ch^48LL);ch=getchar();}
return x*fh;
}
const int maxn=1e5+5;
const int mod=1e5+3;
const long long INF=1000000000000000000ll;
map<long long,int> fr;
long long a[maxn],mmax,mmin;
int ans=1,h[maxn],tot=1,n;
struct hash{
int num,nxt;
long long val;
}b[maxn<<4];
set<long long> s;
int main(){
for(unsigned long long i=1000;i>=2;i--)
{
for(unsigned long long j=i;j<=INF;j*=i)
{
if(j==0)
break;
fr[(long long)j]=i;
}
}
memset(h,-1,sizeof(h));
n=read();
long long lat=0;
int cnt=0;
for(int i=1;i<=n;i++){
a[i]=read();
if(a[i]==lat)
{
cnt++;
}
else
{
lat=a[i];
cnt=1;
}
ans=max(ans,cnt);
}
for(int l=1;l<n;l++)
{
s.clear();
s.insert(a[l]);
mmax=max(a[l],a[l+1]);
mmin=min(a[l],a[l+1]);
if(mmin==mmax || mmax%mmin)
continue;
s.insert(a[l+1]);
int gys=fr[mmax/mmin];
ans=max(ans,2);
for(int r=l+2;r<=n;r++){
if(s.find(a[r])!=s.end())
break;
s.insert(a[r]);
mmax=max(a[r],a[r-1]);
mmin=min(a[r],a[r-1]);
if(mmax%mmin!=0)
break;
if(fr[mmax/mmin]!=gys)
break;
ans=max(ans,r-l+1);
}
}
printf("%d\n",ans);
return 0;
}

17-T2

对一个节点进行一次”精彩操作”付出的时间代价是这个节点到根节点路径上的轻边数量.

根节点自身进行精彩操作的代价一定是0.我们只关注最坏情况下的时间复杂度,求出每个节点进行一次精彩操作的代价,然后在这些代价中取最大值,就得到了这棵树此时的最坏时间复杂度.

每一个非叶节点将在它的所有儿子中等概率随机选择一个作为重儿子.给出一棵树,按上面的方式随机选择重儿子,求最坏时间复杂度的期望 \(mod1e9+7\)

设 \(f[i][j]\) 为以ii为根节点的子树中最坏时间复杂度小于等于jj的概率设 \(g[i][j]\) 为当前扫到的以ii为父亲节点的所有儿子最坏时间复杂度小于等于 \(j\) 的概率之和.

枚举当前哪一个节点充当重儿子、所有儿子、枚举最坏时间复杂度 \(k\).

如果第二重循环中枚举的儿子恰好是重儿子的话,那么父亲节点的最坏时间复杂度为 \(k\) 的情况可以由两种情况转移过来.

1.重儿子的时间复杂度恰好为 \(k\) 的概率乘上其它儿子时间复杂度小于等于 \(k\) 的概率

2.其它儿子的时间复杂度恰好为 \(k\) 的概率乘上重儿子的时间复杂度小于等于 \(k\) 的概率.


#include<bits/stdc++.h>using namespace std;const int N=5000,M=10000,mod=1e9+7;struct bian{
int nxt,to;}b[M];int head[N],cnt,n,son[N],fa[N],root=1,siz[N];long long ans,f[N][N],g[N],h[N];void add(int from,int to){
b[++cnt].nxt=head[from];
b[cnt].to=to;
head[from]=cnt;}int qp(int a,int b){
int rec=1;
while(b>0)
{
if(b&1)
{
rec=1LL*rec*a%mod;
}
a=1LL*a*a%mod;
b>>=1;
}
return rec;}void dfs(int u){
siz[u]=1;
int i=head[u],v;
while(i)
{
v=b[i].to;
if(v!=fa[u])
{
dfs(v);
siz[u]+=siz[v];
}
i=b[i].nxt;
}
int p=qp(son[u],mod-2);
i=head[u];
while(i)
{
v=b[i].to;
if(v==fa[u])
{
i=b[i].nxt;
continue;
}
for(int j=0;j<=n;j++)
g[j]=1;
int j=head[u],v2;
while(j)
{
v2=b[j].to;
if(v2==fa[u])
{
j=b[j].nxt;
continue;
}
for(int k=0;k<=siz[v2]+1;k++)
{
long long qt=g[k],xz=f[v2][k];
if(k)
{
qt-=g[k-1];
xz-=f[v2][k-1];
}
if(v==v2)
{
h[k]=(qt*f[v2][k]%mod+xz*g[k]%mod-xz*qt%mod+mod)%mod;
}
else
{
xz=f[v2][k-1];
if(k>1)
xz-=f[v2][k-2];
h[k]=(qt*f[v2][k-1]%mod+xz*g[k]%mod-xz*qt%mod+mod)%mod;
}
}
g[0]=h[0],h[0]=0;
for(int k=1;k<=siz[v2]+1;k++)
{
g[k]=(g[k-1]+h[k])%mod;
h[k]=0;
}
j=b[j].nxt;
}
for(int j=siz[u];j>=1;j--)
{
g[j]=(g[j]-g[j-1]+mod)%mod;
}
if(u==1)
{
int xxx;
xxx++;
}
for(int j=0;j<=siz[u];j++){
f[u][j]=(f[u][j]+g[j]*p%mod)%mod;
}
i=b[i].nxt;
}
if(son[u]==0)
f[u][0]=1;
for(int i=1;i<=siz[u]+1;i++)
{
f[u][i]=(f[u][i]+f[u][i-1])%mod;
}}int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&son[i]);
for(int j=1,v;j<=son[i];j++)
{
scanf("%d",&v);
fa[v]=i;
add(i,v);
add(v,i);
}
}
while(fa[root])
root=fa[root];
dfs(root);
for(int i=1;i<=n;i++)
{// cout<<i<<" "<<f[root][i]<<" "<<f[root][i-1]<<endl;
ans=(ans+1LL*i*(f[root][i]-f[root][i-1]+mod)%mod+mod)%mod;
}
printf("%lld\n",ans);
return 0;}

17-T3

在学生的联名提议下,HZ决定在校园内建造一个新型游乐场。

游乐场一共有n个项目,在不同的项目之间有一些走廊相连。

为了保证学生能顺利的游玩完游乐场的每个项目,一个建造方案需要满足以下的条件:将游乐园抽象为一张图,把走廊视为无向边,整张图无重边,无自环。可以通过恰好一次操作使得图中存在一条欧拉回路(从某个点出发经过所有边恰好一次并最终回到起点的路径),其中操作可以是添加一条不在图中的边或是删去一条图中的边。要求操作后的图仍满足条件1,且图中任意两点可以互相到达。设计游乐场布局的任务被委托给了学生会会长小R,小R想先统计出所有的方案,但她数数向来数不清,所以你能不能帮小R统计一下一共有多少种建造方案。

一句话题意:求 \(n\)个点任意连无向边组成的欧拉回路种类乘\(C_{n}^{2}\)

引理

1.无向连通图奇数出度的点个数为偶数.

2.度数均为偶数的无向联通图为欧拉回路.

设:

\(f[i]\) 表示 \(i\) 个点任意连无向边组成的欧拉回路种类.

\(g[i]\) 表示 \(i-1\) 个点任意连无向边组成的图种类.显然有 \(g[i]=2^{C_{i}^{2}}\)

\(i-1\) 个点的无向连通图向 \(i\) 点连成欧拉回路的充要条件是,将原图中奇数出度的点与 \(i\) 点连接。

我们运用容斥,\(f[i]=g[i]-\sum_{j=1}^{i-1}f[j]g[i-j]C_{i-1}^{j-1}\)

(总情况减去不连通的)

#include<bits/stdc++.h>
#define N 5000
using namespace std;
const int mod=1e9+7;
inline int read()
{
int s=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
int n;
long long C[N][N],p[N],f[N];
long long qp(long long a,long long b)
{
long long rec=1;
while(b>0)
{
if(b&1)
{
rec*=a;
rec%=mod;
}
a*=a;
a%=mod;
b>>=1;
}
return rec;
}
int main()
{
n=read();
for(int i=0;i<=n;i++)
C[i][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
}
// for(int i=1;i<=n;i++)
// {
// for(int j=1;j<=i;j++)
// {
// printf("%lld ",C[i][j]);
// C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
// }
// puts("");
// }
for(int i=1;i<=n;i++)
p[i]=qp(2,C[i-1][2]);
for(int i=1;i<=n;i++)
{
long long sum=0;
f[i]=p[i];
for(int j=1;j<=i-1;j++)
{
f[i]-=f[j]*p[i-j]%mod*C[i-1][j-1]%mod;
f[i]%=mod;
}
f[i]=((f[i]%mod+mod)%mod+mod)%mod;
}
printf("%lld\n",((f[n]*C[n][2]%mod+mod)%mod+mod)%mod);
return 0;
}
/*
欧拉回路:无向图的每个节点的度数都是偶数度
无向图奇数度的点一定为偶数
n^2递推,f[i]表示i个点的欧拉回路最终图种类数
最终答案为f[n]*C[n][2]
不合法的转移
j个点的欧拉回路
*/

18-T1


#include<bits/stdc++.h>
using namespace std;
const int M=400000,N=200000;
inline int read()
{
    int s=0,w=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
    return s*w;
}
int n,m,cnt,head[N],du[N],zh[N],sumzh;
bool vis[N];
long long ans;
struct bian
{
    int nxt,to;
}b[M];
void add(int from,int to)
{
    b[++cnt].nxt=head[from];
    b[cnt].to=to;
    head[from]=cnt;
}
void dfs(int u,int fa)
{
    vis[u]=1;
    int i=head[u],v;
    while(i)
    {
        v=b[i].to;
        if(!vis[v]&&v!=fa)
        {
            dfs(v,u);
        }
        i=b[i].nxt;
    }
}
int main()
{
    n=read();
    m=read();
    for(int i=1,u,v;i<=m;i++)
    {
        u=read();v=read();
        if(u==v)
        {
            sumzh++;
            zh[u]++;
            continue;
        }
        add(u,v);
        add(v,u);
        du[u]++;
        du[v]++;
    }
    for(int i=1;i<=n;i++)
    {
        if(!vis[i]&&du[i])
        {
            dfs(i,0);
            break;
        }
    }
    bool ok=0;
    for(int i=1;i<=n;i++)
    {
        if(vis[i])
        {
            ok=1;
            break;
        }
    }
    if(ok==0)
        du[1]=1;
    for(int i=1;i<=n;i++)
    {
        if(!vis[i]&&du[i])
        {
            printf("0\n");
            return 0;
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=head[i];j;j=b[j].nxt)
        {
            ans+=du[b[j].to]-1;
        }
    }
    for(int i=1;i<=n;i++)
    {
        ans+=du[i]*sumzh;
    }
    ans+=sumzh*(sumzh-1);
    ans/=2;
    printf("%lld\n",ans);
    return 0;
}
/*
显然起点和重点必须有一条直走一次的边,否则无法走出去
m-2条边允许走2次,把所有边复制一倍,一定欧拉回路
从任何一个点出发,最后回退两条边一定满足目的
答案就是最后推掉的两条边种类
和路上一个自环只走一次的种类*退掉一条边
和路上两个自环只走一次 
非连通图直接白给 
*/ 

18-T2

#include<bits/stdc++.h>
using namespace std;
int n;
long long k,a[1000001],sum,maxn,ans;
int main()
{
    scanf("%d%lld",&n,&k);
    sum=k;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        sum+=a[i];
        maxn=max(maxn,a[i]);
    }
    maxn+=k;
    long long d=1;
    while(1)
    {
        long long flag=0;
        for(int i=1;i<=n;i++)
        {
            if(a[i]%d!=0)
                flag+=(a[i]/d+1)*d;
            else
                flag+=a[i];
        }
        if(flag<=sum)
            ans=d;
        if(maxn<=d)
            break;
        d=sum/(sum/(d+1));
    }
    cout<<ans<<endl;
    return 0;
}
//https://www.cnblogs.com/wwlwQWQ/p/11408950.html

18-T3

设:

\(f[i][j]\) 为i级的超级树有j条路径且无公共点的方案数

则有:

\[\begin{eqnarray} \\&
f[i+1][j+k]=\sum f[i][j]*f[i][k] \\&
f[i+1][j+k+1]=\sum f[i][j]*f[i][k] \\&
f[i+1][j+k]=\sum f[i][j]*f[i][k]*2*(j+k) \\&
f[i+1][j+k-1]=\sum f[i][j]*f[i][k]*C_{j+k}^{2} \\&
\end{eqnarray}\]

以上四种情况分别对应:

1.左子树和右子树不选根节点本身的路径,再加上原本的方案

2.左子树和右子树,选根节点本身的路径,再加上原本的方案

3.左子树的路径连接根节点,右子树的路径连接根节点

连接根节点有两种方案,即从一条路径的头连或者尾连,即根节点作为七点还是终点。所以要乘二。

4.左子树的路径经过根节点连接右子树的路径


#include<bits/stdc++.h>
using namespace std;
unsigned long long f[400][400],n,mod;
int main()
{
    scanf("%llu%llu",&n,&mod);
    f[1][0]=f[1][1]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=(n-i+2);j++)
        {
            for(int k=0;k<=(n-i+2-j);k++)
            {
                f[i+1][j+k]=(f[i+1][j+k]+f[i][j]*f[i][k]%mod)%mod;
                f[i+1][j+k+1]=(f[i+1][j+k+1]+f[i][j]*f[i][k]%mod)%mod;
                f[i+1][j+k]+=f[i][j]*f[i][k]%mod*2%mod*(j+k)%mod;
                f[i+1][j+k]%=mod;
                f[i+1][j+k-1]+=f[i][j]*f[i][k]%mod*(j+k)%mod*(j+k-1)%mod;
                f[i+1][j+k-1]%=mod;
            }
        }
    }
    printf("%llu\n",f[n][1]%mod);
    return 0;
}

NOIP模拟测试17&18的相关教程结束。

《NOIP模拟测试17&18.doc》

下载本文的Word格式文档,以方便收藏与打印。