NOIP提高组模拟赛26

又双叒叕炸了。

A.LCIS

蓝书原题,但是我没有蓝书..

没关系反正水过了

复杂度极其不正确

好在老殷复杂度也不对所以没有上交数据

\(upd:\)好了,看懂了,懒得打了,粘一下

image

考场打的非常诡异,头一回觉得\(vector\)这么好用,大爱vector

\(f[i][j]\)表示\(a\)\(i\)\(b\)\(j\)个的\(LCIS\)

为了优化,我们只使用\(a[i]==b[j]\)\(i\)\(j\)

利用\(vector\)开桶,快速找到所有与\(a[i]\)相等的\(b[j]\)的下标

转移\(f[i][j]=max(f[p][q]+1)\)

要求\(a[p]<a[i]\),并且\(f[p][q]>0\)

过程中不断更新\(ans\)

就是这样,复杂度假的一批,但是过了

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

using namespace std;

typedef long long ll;

const int maxn=3005;
int n,cnt;
ll a[maxn],b[maxn],lsh[maxn<<1|1];
int f[maxn][maxn];
vector<int>v[maxn<<1|1];
int rem[maxn],tot;

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
    for(int i=1;i<=n;++i)scanf("%lld",&b[i]);
    for(int i=1;i<=n;++i)lsh[++cnt]=a[i];
    for(int i=1;i<=n;++i)lsh[++cnt]=b[i];
    sort(lsh+1,lsh+cnt+1);cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
    for(int i=1;i<=n;++i)b[i]=lower_bound(lsh+1,lsh+cnt+1,b[i])-lsh;
    for(int i=1;i<=n;++i)a[i]=lower_bound(lsh+1,lsh+cnt+1,a[i])-lsh;
    for(int i=1;i<=n;++i)v[b[i]].push_back(i);
    int ans=0;
    for(int i=1;i<=n;++i){
        int s=v[a[i]].size();
        if(s==0)continue;
        for(int j=0;j<s;++j)f[i][v[a[i]][j]]=1;
        ans=max(ans,1);
        for(int j=1;j<=tot;++j){
            if(a[rem[j]]>=a[i])continue;
            int ss=v[a[rem[j]]].size();
            for(int p=0;p<s;++p){
                for(int q=0;q<ss;++q){
                    if(v[a[i]][p]<v[a[rem[j]]][q])break;
                    f[i][v[a[i]][p]]=max(f[i][v[a[i]][p]],f[rem[j]][v[a[rem[j]]][q]]+1);
                }
                ans=max(ans,f[i][v[a[i]][p]]);
            }
        }
        rem[++tot]=i;
    }
    printf("%d\n",ans);
    return 0;
}

B. 物流运输

一看这题:这不费用瘤板子题吗?

然后,就没有然后了

最后\(30min\)想到装压解法,开始码,在最后\(3min\)交上了

但是\(MLE\)了,,,,,,我发现边权写的是\(>0\),猜测会炸\(int\),但是没有。。开了\(long long\)的下场是\(30->0\)

对,没错,装压不对

解法是跑多次最短路\(DP\)

枚举上一次什么时候换的航线

每次最短路求的时候屏蔽\(j-i\)天不能走的点

这样其实只需要\(n^2\)次最短路

比装压强多了

code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<iostream>
using namespace std;

int read(){
    char c;int x=0;c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
    return x;
}
typedef long long ll;
const int maxn=23;
const int maxm=805;
int n,m,K,E,tot=1,head[23],ans[106];
bool mp[103][23];
struct edge{int to,net;int w;}e[maxm];
void add(int u,int v,int w){
    e[++tot].net=head[u];
    head[u]=tot;
    e[tot].to=v;
    e[tot].w=w;
}
bool vis[maxn];
int dis[maxn];
queue<int>q;
int spfa(int l,int r){
    memset(vis,0,sizeof(vis));
    for(int i=l;i<=r;++i)
    for(int j=2;j<m;++j)
        if(mp[i][j])vis[j]=1;
    
    memset(dis,0x3f,sizeof(dis));
    dis[1]=0;q.push(1);
    while(!q.empty()){
        int x=q.front();q.pop();vis[x]=0;
        for(int i=head[x];i;i=e[i].net){
            int v=e[i].to;
            if(dis[v]>dis[x]+e[i].w){
                dis[v]=dis[x]+e[i].w;
                if(!vis[v])q.push(v);
            }
        }
    }
    return dis[m];
}
int main(){
    n=read();m=read();K=read();E=read();
    for(int i=1;i<=E;++i){
        int u,v,w;
        u=read();v=read();w=read();       
        add(u,v,w);add(v,u,w);
    }
    int d;scanf("%d",&d);
    for(int i=1;i<=d;++i){
        int P,A,B;
        P=read();A=read();B=read();
        for(int j=A;j<=B;++j)mp[j][P]=1;
    }
    memset(ans,0x3f,sizeof(ans));
    for(int i=1;i<=n;++i){
        int k=spfa(1,i);
        if(k!=dis[m+1])ans[i]=k*i;
        for(int j=1;j<i;++j){
            int k=spfa(j+1,i);
            if(k!=dis[m+1])ans[i]=min(ans[i],ans[j]+K+k*(i-j));
        }
    }
    printf("%d\n",ans[n]);
    return 0;
}

C. tree

奇妙的题,\(Eafoo\)太强了

考场试图随机化,但是一分都没有随到。。

正解注意到边权范围很小,考虑给白边加上一个\(dt\)

\(-100<=dt<=100\)

显然\(dt\)越小,白边越容易被使用,反之亦然

那么这个\(dt\)就可以二分了

但是会有\(dt=i\)\(sumwhileedge>need\)\(dt=i+1\)\(sumwhileedge<need\)

这个时候,直接取一侧,\(sum-need*dt\)即可,取哪一侧好像取决于排序时权相等时白边在前还是黑边在前

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

using namespace std;

typedef long long ll;

const int maxn=50005;
const int maxm=100005;

int V,E,need;
struct edge{int u,v,w,c;}e[maxm];
bool cmp(edge x,edge y){return x.w==y.w?x.c<y.c:x.w<y.w;}
struct SET{
    int f[maxn];
    void pre(int x){for(int i=1;i<=x;++i)f[i]=i;}
    int fa(int x){return f[x]=f[x]==x?x:fa(f[x]);}
    bool hb(int x,int y){x=fa(x);y=fa(y);if(x==y)return false;f[x]=y;return true;}
}S;

int sb,sum;
void work(int dt){
    for(int i=1;i<=E;++i)if(e[i].c==0)e[i].w+=dt;
    sort(e+1,e+E+1,cmp);
    sb=sum=0;int block=V;S.pre(V);
    for(int i=1;i<=E;++i){
        if(S.hb(e[i].u,e[i].v)){
            sum+=e[i].w;
            if(e[i].c==0)++sb;
            --block;if(block==1)break;
        }
    }
    sum=sum-dt*need;
    for(int i=1;i<=E;++i)if(e[i].c==0)e[i].w-=dt;
}
bool check(int dt){
    work(dt);
    if(sb>=need)return true;
    return false;
}
int main(){
    scanf("%d%d%d",&V,&E,&need);
    for(int i=1;i<=E;++i)scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].w,&e[i].c);
    for(int i=1;i<=E;++i)++e[i].u,++e[i].v;
    int ans=0x3f,l=-100,r=100;
    while(l<r){
        if(r-l<=5){
            for(int i=r;i>=l;--i)if(check(i)){ans=sum;break;}break;
        }
        int mid=(l+r)/2;
        if(check(mid))l=mid;
        else r=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}

D. 建造游乐园

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

HZ什么时候这么好了?哦做题啊,那没事了

有点结论题的意思

首先不难发现答案其实是\(n\)个点的联通欧拉图的个数\(\times n*(n-1)/2\)

问题来了\(n\)个点的联通欧拉图的个数怎么求?

不会了

\(g[i]\)表示\(n\)个点构成的所有点的度为偶数的图的数量

\(g[i]=2^{C_{i-1}^2}\)

解释一下,在\(i-1\)个点构成的所有图中,只要将其中所有度数为奇数的点(一定为偶数个)与\(i\)连边就是一个所有点的度为偶数的图,所以就是在\(i-1\)个点中共\(C_{i-1}^2\)条边,每条边可有可无

这样我们需要去掉不连通的情况

先放柿子

\(\displaystyle f[i]=g[i]-\sum_{j=1}^{i-1}f[j]\times g[i-j]\times C_{i-1}^{j-1}\)

解释一下,我们钦定一个点,他在一个大小为\(j\)的联通快内,剩余部分是一个\(i-j\)大小的图,与他在同一个联通块内的点的集合有\(C_{i-1}^{j-1}\)种选法

然后注意用作指数的取模应该模\(\phi p\),(扩展欧拉定理)

但是由于本题的指数位置都很小,所以没有特别处理

code
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int mod=1000000007;
const int maxn=2005;
int n,fac[maxn],inv[maxn],g[maxn],f[maxn];
int qpow(int x,int y){
    int ans=1;
    for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)ans=1ll*ans*x%mod;
    return ans%mod;
}
int get_c(int n,int m){return 1ll*fac[n]*inv[n-m]%mod*inv[m]%mod;}
int main(){
    scanf("%d",&n);
    fac[0]=1;for(int i=1;i<=n;++i)fac[i]=fac[i-1]*1ll*i%mod;
    inv[n]=qpow(fac[n],mod-2);inv[0]=1;
    for(int i=n-1;i>=1;--i)inv[i]=inv[i+1]*1ll*(i+1)%mod;
    for(int i=1;i<=n;++i)g[i]=qpow(2,get_c(i-1,2));
    for(int i=1;i<=n;++i){
        f[i]=g[i];for(int j=1;j<i;++j)f[i]=(f[i]-1ll*f[j]*g[i-j]%mod*get_c(i-1,j-1)%mod+mod)%mod;
    }
    printf("%d\n",(int)(f[n]*1ll*get_c(n,2)%mod));
    return 0;
}


posted @ 2022-06-07 18:06  Chen_jr  阅读(48)  评论(3编辑  收藏  举报