noip模拟题11.15 距noip2016还剩三天

来源:互联网 发布:笔记本电脑周边 知乎 编辑:程序博客网 时间:2024/06/05 11:06

讲道理,今天考试从开始一直到过了两个小时,我都没有开始码代码。怎么说呢…这貌似是洛谷的官方比赛的原赛,而我居然差点爆零…这套题做着无感啊……

T1 小L的二叉树

【题目描述】

勤奋又善于思考的小L接触了信息学竞赛,开始的学习十分顺利。但是,小L对数据结构的掌握实在十分渣渣。
所以,小L当时卡在了二叉树。 在计算机科学中,二叉树是每个结点最多有两个子结点的有序树。通常子结点被称作“左孩子”和“右孩子”。二叉树被用作二叉搜索树和二叉堆。随后他又和他人讨论起了二叉搜索树。什么是二叉搜索树呢?二叉搜索树首先是一棵二叉树。设key[p]表示结点p上的数值。对于其中的每个结点p,若其存在左孩子lch,则key[p]>key[lch];若其存在右孩子rch,则key[p]<key[rch];注意,本题中的二叉搜索树应满足对于所有结点,其左子树中的key小于当前结点的key,其右子树中的key大于当前结点的key。(因为小L十分喜欢装xx,所以这里他十分装xx的给大家介绍了什么是二叉树和二叉搜索树)。 可是善于思考的小L不甘于只学习这些基础的东西。他思考了这样一个问题:现在给定一棵二叉树,可以任意修改结点的数值。修改一个结点的数值算作一次修改,且这个结点不能再被修改。若要将其变成一棵二叉搜索树,且任意时刻结点的数值必须是整数(可以是负整数或0),所要的最少修改次数。 这一定难不倒聪明的你吧!如果你能帮小L解决这个问题,也许他会把最后的资产分给你1/16哦!

【输入格式】

第一行一个正整数n表示二叉树节点数。节点从1~n进行编号。 第二行n个正整数用空格分隔开,第i个数ai表示结点i的原始数值。 此后n - 1行每行两个非负整数fa, ch,第i + 2行描述结点i + 1的父亲编号fa,以及父子关系ch,(ch = 0 表示i + 1为左儿子,ch = 1表示i + 1为右儿子)。 为了让你稍微减轻些负担,小L规定:结点1一定是二叉树的根哦! 【输出格式】 仅一行包含一个整数,表示最少的修改次数。

【样例输入】

3
2 2 2
1 0
1 1

【样例输出】

2

【数据范围】

20 % :n <= 10 , ai <= 100
40 % :n <= 100 , ai <= 200
60 % :n <= 2000 . 100 % :n <= 10 ^ 5 , ai < 2 ^ 31

感觉这道题的题意描述不清楚。正解是将二叉树的中序遍历求出来再求一个lis,但这就意味着一个节点必须比它的左子树中的任何一个节点的权值大,比它右子树任何一个节点的权值小,但题中只说了左儿子右儿子。
中序遍历求出后还需注意一点,就是整数的限制。比如说某个最长上升序列是这样:1 _ 2,中间有一个数,题意是说将那个数改成1-2中的树,但显然没有这样的整数。处理方法是:就这个中序遍历的每一个都减去它的下标。这样可以保证第 i 个数至少比第 j 个数 大 i-j。还有一点,减去下标后队列优化要用upper——bound,相同也满足。
代码:

#include<queue>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;int que[100005],ans,b[100005],g[100005];int tot,lson[100005],rson[100005],a[100005];inline int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}inline void dfs(int k){    if(lson[k])dfs(lson[k]);    que[++tot]=a[k];    if(rson[k])dfs(rson[k]);}int main(){    freopen("tree.in","r",stdin);    freopen("tree.out","w",stdout);    int n=read();    for(int i=1;i<=n;i++)        a[i]=read();    for(int i=2;i<=n;i++)    {        int q=read(),p=read();        if(p==0)lson[q]=i;        else rson[q]=i;    }    dfs(1);int ans=0;    for(int i=1;i<=tot;i++)        que[i]-=i;    memset(g,127,sizeof g);g[0]=-0x7f7f7f7f;    for(int i=1;i<=tot;i++)    {        int k=upper_bound(g,g+tot+1,que[i])-g-1;        b[i]=k+1;g[k+1]=min(g[k+1],que[i]);        ans=max(ans,b[i]);    }    printf("%d",tot-ans);    return 0;}

T2 小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很快就能发现它是不能修建好的。因此她希望得到你的帮助,找出无法修建的最大围栏长度。 这一定难不倒聪明的你吧!如果你能帮小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

将所有可以使用的木板(包括减去1-m的)保存下来,然后完全背包,顺推,判断每个长度是否可以满足,至于上界…我不知道,考试时上界太大,T了几组…看数据貌似38000左右…这也算是cheat吧…由于数据水,完全背包可以过完。但正解要用同余最短路…写不来啊…
代码:

#include<ctime>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#ifdef WIN32#define AUTO "%I64d"#else#define AUTO "%lld"#endifusing namespace std;int bbbb,maxn;inline int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}int a[38815],bb[38815],tot,que[38815];inline void dfs(int k){    if(k<=0)return ;    if(bb[k]){        bbbb=1;return ;    }    for(int i=1;i<=tot;i++)    {        if(bb[k-que[i]]==1)            bb[k]=1;    }    if(!bb[k])maxn=k;}int cmp(int a,int b){    return a>b;}int main(){    freopen("bullpen.in","r",stdin);    freopen("bullpen.out","w",stdout);    int n=read(),m=read(),minx=0x7f7f7f7f;    for(int i=1;i<=n;i++)    {        int a=read();        for(int i=0;i<=m;i++)        {            if(a-i<=0)break;            if(!bb[a-i])            {                bb[a-i]=1;                que[++tot]=a-i;            }            minx=min(a-i,minx);        }    }    sort(que+1,1+tot+que,cmp);    if(bb[1])    {        printf("-1");        return 0;    }    for(int i=1;i<=38810;i++)    {        bbbb=0;        dfs(i);    }    printf("%d",maxn);    return 0;}

T3 小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
80%~100% :T <= 10, 1<= N<= 1000000000, 0<=K<=30

表示考试时我想出来的方程和40%dp方程就差一点…
30%递推,f[i][j]表示长度为i 且由j 种组成的方案数;
那么f[i][j]=f[i-1][j-1]*(k-j+1)(表示长度为i 时选择j 种颜色,必须在(k-j+1)里选择)+f[i-1][j]*j(同理了)
70%矩阵乘法优化上式+快速幂复杂度(logN * 矩阵乘法复杂度)
100%:容斥原理
不会矩阵,数论渣渣,我多半算是个废人了…
30%:

#include<queue>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#ifdef WIN32#define AUTO "%I64d"#else#define AUTO "%lld"#endifusing namespace std;const int mod=1234567891;long long f[100005][35];inline int read(){    int x=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}int main(){    freopen("pearl.in","r",stdin);    freopen("pearl.out","w",stdout);    int T=read();    while(T--)    {        int n=read(),k=read();        memset(f,0,sizeof f);        for(int i=0;i<=n;i++)f[i][0]=1;        f[1][1]=k;        for(int i=1;i<=n;i++)            for(int j=1;j<=min(k,i);j++)                f[i][j]=((f[i-1][j]*j)%mod+(f[i-1][j-1]*(k-j+1))%mod)%mod;        printf(AUTO,f[n][k]);        printf("\n");    }    return 0;}

马上就要noip了,我感到了恐慌…

0 1
原创粉丝点击