NOIP提高组模拟赛26


又双叒叕炸了。

A.LCIS

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

没关系反正水过了

复杂度极其不正确

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

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

考场打的非常诡异,头一回觉得\(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],并且\(f[p][q]>0\)

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

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

code
#include
#include
#include
#include

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];
vectorv[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=a[i])continue;
            int ss=v[a[rem[j]]].size();
            for(int p=0;p

B. 物流运输

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

然后,就没有然后了

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

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

对,没错,装压不对

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

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

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

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

比装压强多了

code
#include
#include
#include
#include
#include
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];
queueq;
int spfa(int l,int r){
    memset(vis,0,sizeof(vis));
    for(int i=l;i<=r;++i)
    for(int j=2;jdis[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

C. tree

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

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

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

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

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

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

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

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

code
#include
#include
#include

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=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=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
#include
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