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
在修改的时候因为要保证整数,所以将队列中每个的值-i保证严格递增,-i后求最长不下降序列。
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+5;
int n,ans,dui[N],top,b[N],tot;
struct node{
int le,ri,ty,fa,wor;
int ma[2];
}tr[N];
void dfs(int k)
{
if(tr[k].le)dfs(tr[k].le);
dui[++top]=tr[k].wor-top; //使其严格递增
if(tr[k].ri)dfs(tr[k].ri);
}
int sear(int k)
{
int w=0;
int le=1,ri=tot;
while(le<=ri)
{
int mid=(le+ri)>>1;
if(b[mid]<=k)w=mid,le=mid+1; //不下降
else ri=mid-1;
}
return w+1;
}
void worrk()
{
for(int i=1;i<=n;i++)
{
if(dui[i]>=b[tot])b[++tot]=dui[i];
else{
int k=sear(dui[i]);
b[k]=dui[i];
}
}
ans=n-tot;
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&tr[i].wor);
for(int i=2;i<=n;i++)
{
scanf("%d%d",&tr[i].fa,&tr[i].ty);
if(tr[i].ty==0) tr[tr[i].fa].le=i;
else tr[tr[i].fa].ri=i;
}
dfs(1);
worrk();
printf("%d",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
这道题正解貌似是SPFA?
我用的一种神奇的暴力法反正….
自己测过了,vijos上没过完…懒得改…
大概做法是:由0开始向外推活动区间(可拼得得区间),用线段树进行染色合并√。
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define L(x) (x<<1)
#define R(x) (x<<1|1)
using namespace std;
const int N=3e5+5;
int n,m,qj[N],top,y,ma,ans,len[105],fx[N],kk[N][2],tt;
struct node{
int l,r;
bool sym,ss;
}tr[N*4];
bool comp(const int &a,const int &b)
{return a<b;}
void build(int u,int le,int ri)
{
tr[u].l=le,tr[u].r=ri;
if(le==ri){return;}
int mid=(le+ri)>>1;
build(L(u),le,mid);
build(R(u),mid+1,ri);
}
void down(int u)
{
if(!tr[L(u)].sym){tr[L(u)].sym=true;tr[L(u)].ss=true;}
if(!tr[L(u)].sym){tr[L(u)].sym=true;tr[L(u)].ss=true;}
tr[u].ss=false;
}
void sear(int u,int k,int flag)
{
if(tr[u].sym)
{
if(flag==0)y=tr[u].l;
else y=tr[u].r;
return;
}
if(tr[u].l==tr[u].r)return;
if(tr[u].ss)down(u);
int mid=(tr[u].l+tr[u].r)>>1;
if(mid>=k)sear(L(u),k,flag);
else sear(R(u),k,flag);
}
void col(int u,int le,int ri)
{
if(tr[u].l==le&&tr[u].r==ri)
{
qj[le]=ri;fx[ri]=le;
tr[u].sym=true;
tr[u].ss=true;
return;
}
if(tr[u].ss)down(u);
int mid=(tr[u].l+tr[u].r)>>1;
if(mid>=ri)col(L(u),le,ri);
else if(mid<le)col(R(u),le,ri);
else {
col(L(u),le,mid);
col(R(u),mid+1,ri);
}
}
int sear2(int u)
{
if(tr[u].sym==true)
return -1;
if(tr[u].l==tr[u].r)
{
if(tr[u].sym==false)return tr[u].l;
else return -1;
}
if(tr[u].ss)down(u);
int w=sear2(R(u));
if(w!=-1)return w;
else return sear2(L(u));
}
int sse(int u,int le,int ri)
{
if(tr[u].l==le&&tr[u].r==ri)
return sear2(u);
int mid=(tr[u].l+tr[u].r)>>1;
if(mid>=ri)sse(L(u),le,ri);
else if(mid<le)sse(R(u),le,ri);
else return max(sse(L(u),le,mid),sse(R(u),mid+1,ri));
}
void worrk()
{
build(1,1,N-5);
if(len[1]-m<=1){ans=-1;return;}
for(int j=0;j<=N-5;j++)
if(j==0||qj[j])
{
for(int i=1;i<=n;i++)
{
int ll=max(1,j+max(0,len[i]-m)),rr=qj[j]+len[i];
y=-1;sear(1,ll,0);if(y!=-1)ll=y;
y=-1;sear(1,rr,1);if(y!=-1)rr=y;
while(qj[rr+1]&&rr<N-5)rr=qj[rr+1];
while(ll>1&&fx[ll-1])ll=fx[ll-1];
if(ll<=N-5)col(1,ll,min(rr,N-5));
if(rr-ll>=len[1]){ans=sse(1,1,min(ll-1,N-5));return;}
kk[++tt][0]=ll;kk[tt][1]=rr;
}
j=qj[j];
}
ans=sse(1,1,N-5);
}
int main()
{
freopen("bullpen.in","r",stdin);
freopen("bullpen.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{scanf("%d",&len[i]);ma+=len[i];}
sort(len+1,len+n+1,comp);
worrk();
cout<<ans<<endl;
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时限 :1000ms 80%~100% :T <= 10, 1<= N<= 1000000000, 0<=K<=30 时限:50ms
此题属于dp中比较好(可)想(爱)的了吧,
f[i][j]=f[i-1][j-1]*(k-j+1)(表示长度为i 时选择j 种颜色,必须在(k-j+1)里选
择)+f[i-1][j]*j(同理了)
这个式子的i比较长而且递推有规律,于是乎顺其自然地考虑矩阵,当然只有70%的分的。
注意最后答案是∑(j=k~n)f[j][k];
代码1:
从f[1]开始向后推,将sum放在0排。a[0][k]=a[0][0]=1。
#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
const int mod=1234567891;
int n,k,t,an;
struct node{
int a[31][31];
};
node ans,pre;
node sum(node x,node y,int r1,int r2)
{
node w;
for(int i=0;i<=r1;i++)
for(int j=0;j<=r2;j++)
{
w.a[i][j]=0;
for(int q=0;q<=k;q++)
w.a[i][j]=((LL)w.a[i][j]+((LL)x.a[i][q]*(LL)y.a[q][j])%mod)%mod;
}
return w;
}
node ksm(node x,int y)
{
if(y==0)return x;
if(y==1)return x;
node w=ksm(x,y/2);
node d=sum(w,w,k,k);
if(y&1)d=sum(d,x,k,k);
return d;
}
int main()
{
freopen("pearl.in","r",stdin);
freopen("pearl.out","w",stdout);
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
scanf("%d%d",&n,&k);
memset(ans.a,0,sizeof(ans.a));
memset(pre.a,0,sizeof(pre.a));
if(n>1)for(int i=1;i<=k;i++)
{
pre.a[i][i]=i;
if(i>1)pre.a[i][i-1]=k-i+1;
}
pre.a[0][k]=1;pre.a[0][0]=1;
pre=ksm(pre,n-1);
ans.a[1][0]=k;
ans=sum(pre,ans,k,0);
an=((LL)ans.a[0][0]+(LL)ans.a[k][0])%mod;
printf("%d\n",an);
}
}
代码2:
从0开始推,f[0][0]=1,sum放在第k+1排,即a[k+1][k]=a[k+1][k+1]=1。
#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
const int mod=1234567891;
int n,k,t,an;
struct node{
int a[32][32];
};
node ans,pre;
node sum(node x,node y,int r1,int r2)
{
node w;
memset(w.a,0,sizeof(w.a));
for(int i=0;i<=r1;i++)
for(int j=0;j<=r2;j++)
{
for(int q=0;q<=r1;q++)
w.a[i][j]=((LL)w.a[i][j]+((LL)x.a[i][q]*(LL)y.a[q][j])%mod)%mod;
}
return w;
}
node ksm(node x,int y)
{
if(y==1)return x;
node w=ksm(x,y/2);
node d=sum(w,w,k+1,k+1);
if(y&1)d=sum(d,x,k+1,k+1);//!!!*x
return d;
}
int main()
{
freopen("pearl.in","r",stdin);
freopen("pearl.out","w",stdout);
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
scanf("%d%d",&n,&k);
memset(ans.a,0,sizeof(ans.a));
memset(pre.a,0,sizeof(pre.a));
for(int i=1;i<=k;i++)
{
pre.a[i][i]=i;
pre.a[i][i-1]=k-i+1;
}
pre.a[k+1][k]=1;pre.a[k+1][k+1]=1;
pre=ksm(pre,n);
ans.a[0][0]=1;
ans=sum(pre,ans,k+1,0);
an=((LL)ans.a[k+1][0]+(LL)ans.a[k][0])%mod;
printf("%d\n",an);
}
}