[NOIP模拟题][LIS][同余最短路][DP][矩阵快速幂][容斥原理]

小L的二叉树
【题目描述】
勤奋又善于思考的小L接触了信息学竞赛,开始的学习十分顺利。但是,小L对数据结构的掌握实在十分渣渣。
所以,小L当时卡在了二叉树。 在计算机科学中,二叉树是每个结点最多有两个子结点的有序树。通常子结点被称作“左孩子”和“右孩子”。二叉树被用作二叉搜索树和二叉堆。随后他又和他人讨论起了二叉搜索树。什么是二叉搜索树呢?二叉搜索树首先是一棵二叉树。设key[p]表示结点p上的数值。对于其中的每个结点p,若其存在左孩子lch,则key[p]>key[lch];若其存在右孩子rch,则key[p]

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
const int maxn=1e5+5;
const int INF=0x7f7f7f7f;
int ch[maxn][2],rank[maxn],w[maxn],dfn[maxn],cnt,tot,n,g[maxn],dp[maxn],ans,flag;
int readint()
{
    int x=0; char ch=getchar();
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();}
    return x;
}
void dfs(int u)
{
    if (ch[u][0]) dfs(ch[u][0]);
    rank[++cnt]=u; w[u]-=cnt;
    if (ch[u][1]) dfs(ch[u][1]);
}
int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    n=readint();
    for (int i=1;i<=n;i++) w[i]=readint();
    for (int i=2;i<=n;i++)
    {
        int x=readint(),flag=readint();
        ch[x][flag]=i;
    }
    dfs(1);
    memset(g,INF,sizeof(g));
    g[0]=-INF;
    for (int i=1;i<=n;i++)
    {
        int pos=upper_bound(g,g+n+1 ,w[rank[i]])-g-1;
        dp[i]=pos+1;
        g[dp[i]]=min(g[dp[i]],w[rank[i]]);
    }
    for (int i=1;i<=n;i++) ans=max(ans,dp[i]);
    ans=n-ans;
    printf("%d",ans);
    return 0;
}

小L的牛栏
【题目描述】 小L通过泥萌的帮助,成功解决了二叉树的修改问题,并因此写了一篇论文, 成功报送了叉院(羡慕不?)。勤奋又勤思的他在研究生时期成功转系,考入了北京大学光华管理学院!毕业后,凭着自己积累下的浓厚经济学与计算机学的基础,成功建设了一个现代化奶牛场! 奶牛们十分聪明,于是在牛场建围栏时打算和小L斗智斗勇!小L有N种可以建造围栏的木料,长度分别是l1,l2„lN,每种长度的木料无限。
修建时,他将把所有选中的木料拼接在一起,因此围栏的长度就是他使用的木料长度之和。但是聪明的小L很快发现很多长度都是不能由这些木料长度相加得到的,于是决定在必要的时候把这些木料砍掉一部分以后再使用。
不过由于小L比较节约,他给自己规定:任何一根木料最多只能削短M米。当然,每根木料削去的木料长度不需要都一样。不过由于测量工具太原始,小L只能准确的削去整数米的木料,因此,如果他有两种长度分别是7和11的木料,每根最多只能砍掉1米,那么实际上就有4种可以使用的木料长度,分别是6, 7,10, 11。
因为小L相信自己的奶牛举世无双,于是让他们自己设计围栏。奶牛们不愿意自己和同伴在游戏时受到围栏的限制,于是想刁难一下小L,希望小L的木料无论经过怎样的加工,长度之和都不可能得到他们设计的围栏总长度。不过小L知道,如果围栏的长度太小,小L很快就能发现它是不能修建好的。因此她希望得到你的帮助,找出无法修建的最大围栏长度。的资产分给你1/8哦! 【输入格式】
输入的第一行包含两个整数N, M,分别表示木料的种类和每根木料削去的最大值。以下各行每行一个整数li(1< li< 3000),表示第i根木料的原始长度。 【输出格式】
输出仅一行,包含一个整数,表示不能修建的最大围栏长度。如果任何长度的围栏都可以修建或者这个最大值不存在,输出-1。 【样例输入】 2 1 7 11 【样例输出】 15 【数据范围】 40 % :1< N<10, 0< M< 300 100 % :1< N< 100, 0< M< 3000

有趣的题
首先40%很容易想到完全背包,手工设一Max上限作为体积
100%就很神了,同余最短路
预处理出所有棒子能够通过切割得出的长度,取最小的长度为p,如果p为1直接输出-1
然后我们建p-1个集合,集合i表示mod p=1的长度的集合,假设集合i中最小元素为min,很明显,min通过加p,2p,3p…可以推出集合i中所有大于min的元素,我们只需要找出所有能够组合出的i中的最小元素,那么答案即为max(min-p)
具体的组合通过spfa实现就可以了

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=100+5;
const int maxm=3e3+5;
const int INF=0x3f3f3f3f;
int n,m,cnt,num[maxm],dis[maxm],p,ans;
bool vis[maxm],inq[maxm];
queue<int>que;
int readint()
{
    int x=0; char ch=getchar();
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();}
    return x;
}
void spfa()
{
    memset(dis,INF,sizeof(dis));
    que.push(0); dis[0]=0;
    while (!que.empty())
    {
        int u=que.front(); que.pop(); inq[u]=false;
        for (int i=2;i<=cnt;i++)
        {
            int v=(u+num[i])%p;
            if (dis[v]>dis[u]+num[i])
            {
                dis[v]=dis[u]+num[i];
                if (inq[v]) continue;
                que.push(v); inq[v]=true;
            }
        }
    }
}
int main()
{
//  freopen("bullpen.in","r",stdin);
//  freopen("bullpen.out","w",stdout);
    n=readint(); m=readint();
    for (int i=1;i<=n;i++)
    {
        int temp=readint(),lim=min(temp,m);
        for (int i=0;i<=lim;i++) vis[temp-i]=true;
    }
    for (int i=1;i<=maxm;i++) if (vis[i]) num[++cnt]=i;
    p=num[1];
    if (p==1)
    {
        printf("-1");
        return 0;
    }
    spfa();
    for (int i=1;i<p;i++)
    {
        if (dis[i]==INF)
        {
            printf("-1");
            return 0;
        }
        ans=max(ans,dis[i]);
    }
    printf("%d",ans-p);
    return 0;
}

小L的珍珠挂饰
【题目描述】 小L通过泥萌的帮助,成功解决了牛栏的修建问题。奶牛们觉得主人非常厉害,于是再也不敢偷懒,母牛们奋力挤奶,生娃。子子孙孙无穷匮也!小L于是成为了一代富豪! 但是一直困扰小L的就是单身问题!小L经过长久的寻觅,小L终于找到了一个心仪的漂亮妹子。于是,小L打算在520那天给妹子一个惊喜!(虽然小L很节约,但是对妹子还是很阔绰的!) 小L决定用K种珍珠为妹子做一串举世无双的珍珠垂饰。珍珠垂饰是由珍珠连接而成的,其长度可以认为就是珍珠垂饰上珍珠的个数。小L现在腰缠万贯,每种珍珠他都拥有N颗。根据将珍珠垂饰打开后珍珠不同的排列顺序可以区别不同种类的项链。现在,小L好奇自己可以组成多少种长度为1至N的不同的珍珠垂饰?当然,为显富有,每串珍珠垂饰都要必须由K种珍珠连成。 答案取模1234567891。 这一定难不倒聪明的你吧!如果你能帮小L解决这个问题,也许他会把最后的资产分给你1/4哦! 【输入格式】
输入包含多组数据。第一行是一个整数T,表示测试数据的个数。每组数据占一行,包含两个整数N和K,用一个空格隔开。 【输出格式】
每组数据输出仅一行,包含一个整数,表示项链的种类数。 【样例输入】 2 2 1 3 2 【样例输出】 2 8 【数据范围】40 % :1<= N<=100000, 0<=K<=30 70 % :1<= N<= 1000000000, 0<=K<=30时限 :1000ms 80%~100% :T <= 10, 1<= N<= 1000000000, 0<=K<=30 时限:50ms

考试没有推出正确的式子,真是蛋疼
dp[i][j]表示i长度用了j种
f[i][j]=f[i-1][j-1]*(k-j+1)+f[i-1][j]*j
矩阵快速幂转移即可
人生第一次写矩阵,值得纪念
容斥原理的坑以后再填,估计NOIP之前很困难了

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else 
#define AUTO "%lld"
#endif 
#define LL long long 
using namespace std;
const int mod=1234567891;
const int maxm=30+5;
int T,n,m;
struct Matrix
{
    LL w[maxm][maxm];
    void clear()
    {
        memset(w,0,sizeof(w));
    }
}A,orgin,ans;
int readint()
{
    int x=0; char ch=getchar();
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();}
    return x;
}
void create()
{
    A.w[1][1]=1;
    for (int i=1;i<=m;i++) A.w[i+1][i]=m-i+1,A.w[i+1][i+1]=i;
}
Matrix multi(Matrix a,Matrix b)
{
    Matrix ret;
    memset(ret.w,0,sizeof(ret.w));
    for (int i=1;i<=m+1;i++)
      for (int k=1;k<=m+1;k++)
        if (a.w[i][k])
          for (int j=1;j<=m+1;j++)
            ret.w[i][j]=(ret.w[i][j]+a.w[i][k]*b.w[k][j]%mod)%mod;
    return ret;
}
Matrix qpow(Matrix a,int p)
{
    Matrix ret=a; p--;
    while (p)
    {
        if (p&1) ret=multi(ret,a);
         a=multi(a,a);
         p>>=1;
    }
    return ret;
}
void print()
{
    for (int i=1;i<=m+1;i++)
    {
        for (int j=1;j<=m+1;j++) printf(AUTO" ",A.w[i][j]);
        putchar('\n');
    }
    putchar('\n');

}
int main()
{
    freopen("pearl.in","r",stdin);
    freopen("pearl.out","w",stdout);
    T=readint(); orgin.w[1][1]=1;
    while (T--)
    {
        n=readint(); m=readint(); A.clear(); create(); 
        ans=multi(qpow(A,n),orgin);
        printf(AUTO,ans.w[m+1][1]); putchar('\n');
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
NOIP(全国青少年信息学奥林匹克竞赛)是中国国内最高水平的信息学竞赛之一,是计算机领域的重要赛事。针对NOIP模拟题,通常是为了帮助参赛者熟悉比赛形式和题型,提供足够的训练机会。 数据下载是NOIP比赛中的一个重要环节,因为参赛者需要根据提供的数据集进行程序开发和测试。数据下载一般通过网络进行,参赛者需要在指定的时间段内下载数据集到自己的计算机上。 在进行NOIP模拟题数据下载时,我们可以按照以下步骤进行操作: 1. 确认下载链接:NOIP官方会提供下载数据集的链接或者FTP地址,参赛者需要确认链接是否可用和正确。 2. 选择下载工具:根据自己的需求,参赛者可以选择合适的下载工具进行操作。常见的下载工具有浏览器内置下载工具、迅雷、IDM等,可以根据个人的习惯和需求选择合适的下载工具。 3. 打开下载工具:根据所选择的下载工具类型,打开对应的软件,进入下载界面。 4. 输入下载链接:将NOIP提供的数据集下载链接复制粘贴到下载工具的链接输入框中,点击确定或开始进行下载。 5. 等待下载完成:根据数据集的大小和网络速度不同,下载时间会有所变化。参赛者需要耐心等待下载完成,确保数据集完整地保存到自己的计算机上。 6. 验证数据完整性:下载完成后,参赛者需要验证数据集的完整性,确保所有文件都成功地保存到指定位置。可以进行文件大小的比对或者逐个文件的校验来检查数据完整性。 通过以上步骤,参赛者可以成功地进行NOIP模拟题数据的下载。在实际比赛中,一个高效的数据下载过程可以提高参赛者的准备时间和竞争力,确保能够充分利用所提供的数据集进行开发和测试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值