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

小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就可以了。
a[i]-a[j] <= i-j ==> a[i]-i <= a[j]-j

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
template <class T> inline void read(T &x)
{
    x = 0;
    T flag = 1;
    char ch = (char)getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch == '-') flag = -1;
        ch = (char)getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x = (x<<1) + (x<<3) + ch - '0';
        ch = (char)getchar();
    }
    x *= flag;
}
template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); }
const int INF=0x3f3f3f3f;
const int maxn = 100005;
struct Node
{
    int ch[2];
    int val;
}node[maxn];
#define ch(x,d) node[x].ch[d]
#define val(x) node[x].val
int a[maxn];
int n;
void in_order(int root)
{
    if(!root) return;
    in_order(ch(root,0));
    a[++n] = val(root);
    a[n] -= n;
    in_order(ch(root,1));
}
inline void init()
{
    read(n);
    for(int i=1;i<=n;i++) read(val(i));
    for(int i=2;i<=n;i++)
    {
        int fa,id;
        read(fa); read(id);
        ch(fa,id) = i;
    }
    n = 0;
    in_order(1);
}
vector <int> d;
int work()
{
    for(int i=1;i<=n;i++)
    {
        vector <int> ::iterator it = upper_bound(d.begin(),d.end(),a[i]);
        if(it == d.end()) d.push_back(a[i]);
        else (*it) = a[i];
    }
    return d.size();
}
int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    init();
    int ans = work();
    printf("%d",n - 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很快就能发现它是不能修建好的。因此她希望得到你的帮助,找出无法修建的最大围栏长度。 这一定难不倒聪明的你吧!如果你能帮小L解决这个问题,也许他会把最后的资产分给你1/8哦!
【输入格式】
输入的第一行包含两个整数N, M,分别表示木料的种类和每根木料削去的最大值。以下各行每行一个整数li(1< li< 3000),表示第i根木料的原始长度。
【输出格式】
输出仅一行,包含一个整数,表示不能修建的最大围栏长度。如果任何长度的围栏都可以修建或者这个最大值不存在,输出-1。
【样例输入】 2 1 7 11
【数据范围】
40 % :1< N<10, 0< M< 300
100 % :1< N< 100, 0< M< 3000


由于如果选定一个最小的可以得到的长度,那么任意当前长度加上若干个最短长度都是可以得到的。
那么dp[i]表示长度模为i的最短长度。
也就是说如果模意义下dp[i]是最小的可以表示的长度,小于它的都不可以被表示,那么ans = max{0<=i < mod | dp[i]} - mod

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
template <class T> inline void read(T &x)
{
    x = 0;
    T flag = 1;
    char ch = (char)getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch == '-') flag = -1;
        ch = (char)getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x = (x<<1) + (x<<3) + ch - '0';
        ch = (char)getchar();
    }
    x *= flag;
}
template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); }
const int INF=0x3f3f3f3f;
const int maxn = 3005;
const int N = 3000;
bool vis[maxn];
int stick[maxn],cnt,mod;
int n,m;
inline void init()
{
    read(n); read(m);
    for(int i=1;i<=n;i++)
    {
        int tmp;
        read(tmp);
        for(int j=0;j<=min(m,tmp-1);j++) vis[tmp-j] = true;
    }
    for(int i=1;i<=N;i++) if(vis[i]) stick[++cnt] = i;
    if(stick[1] == 1) { printf("-1"); exit(0); }
    else mod = stick[1];
}
int dis[maxn];
bool inque[maxn];
queue <int> que;
void spfa()
{
    while(!que.empty()) que.pop();
    memset(dis,0x3f,sizeof(dis));
    memset(inque,0x00,sizeof(inque));
    que.push(0); inque[0]=true; dis[0]=0;
    while(!que.empty())
    {
        int u = que.front(); que.pop(); inque[u]=false;
        for(int i=2;i<=cnt;i++)
        {
            int v = (u + stick[i]) % mod;
            if(dis[v] > dis[u] + stick[i])
            {
                dis[v] = dis[u] + stick[i];
                if(inque[v]) continue;
                inque[v] = true;
                que.push(v);
            }
        }
    }
}
int work()
{
    int ans = 0;
    for(int i=0;i<mod;i++) smax(ans,dis[i]);
    return ans - mod;
}
int main()
{
    freopen("bullpen.in","r",stdin);
    freopen("bullpen.out","w",stdout);
    init();
    spfa();
    int ans = work();
    printf("%d",ans);
    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


前70%:
dp(i,j) = dp(i-1,j) * j + dp(i-1,j-1) * j
用矩阵快速幂转移,可以求前缀和,也可以改变dp的意义,直接表示前缀和。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned U;
template <class T> inline void read(T &x)
{
    x = 0;
    T flag = 1;
    char ch = (char)getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch == '-') flag = -1;
        ch = (char)getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x = (x<<1) + (x<<3) + ch - '0';
        ch = (char)getchar();
    }
    x *= flag;
}
template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); }
const int maxn = 33;
const U mod = 1234567891;
int N,K;
struct Matrix
{
    U w[maxn][maxn];
    Matrix () { memset(w,0,sizeof(w)); }
    Matrix operator * (const Matrix &t)
    {
        Matrix ret;
        for(int i=1;i<=K+1;i++)
            for(int p=1;p<=K+1;p++) if(w[i][p])
                for(int j=1;j<=K+1;j++)
                    (ret.w[i][j] += (U) ((ULL) w[i][p] * t.w[p][j] % mod)) %= mod;
        return ret;
    }
    void operator *= (Matrix &t) { (*this) = t * (*this); }
};
Matrix quick_exp(Matrix a,int p)
{
    Matrix ans;
    for(int i=1;i<=K+1;i++) ans.w[i][i] = 1;
    while(p)
    {
        if(p&1) ans *= a;
        p >>= 1;
        a *= a;
    }
    return ans;
}
int main()
{
    freopen("pearl.in","r",stdin);
    freopen("pearl.out","w",stdout);
    int T; read(T);
    while(T--)
    {
        read(N); read(K);
        if(!K) { printf("0\n"); continue; }
        Matrix ans; ans.w[1][1] = 1; ans.w[K+1][1] = K==1? 1 : 0; // start with S1 = 0 for dp(1,K) = 0
        Matrix delta;
        for(int k=1;k<=K;k++) delta.w[k][k-1] = delta.w[k][k] = k;
        delta.w[1][0] = 0; delta.w[K+1][K-1] = delta.w[K+1][K] = K; delta.w[K+1][K+1] = 1;
        delta = quick_exp(delta,N-1);
        ans *= delta;
        printf("%u\n",ans.w[K+1][1]);
    }
    return 0;
}

100%: 容斥原理+等比数列求和
如果直接是k^n,那么只有k-1种颜色的多计算,同时减去k-1的时候又多减了只有k-2个的情况,容斥解决。
由于答案是长度为1~n的方案数总和,那么就用等比数列求每一项的和,再容斥即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned U;
template <class T> inline void read(T &x)
{
    x = 0;
    T flag = 1;
    char ch = (char)getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch == '-') flag = -1;
        ch = (char)getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x = (x<<1) + (x<<3) + ch - '0';
        ch = (char)getchar();
    }
    x *= flag;
}
template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); }
const int INF=0x3f3f3f3f;
const int maxn = 35;
const int N = 32;
const U mod = 1234567891u;
U C[maxn][maxn];
U inv[maxn];
inline void init()
{
    C[0][0] = 1;
    C[1][0] = C[1][1] = 1;
    for(int i=2;i<=N;i++)
        for(int j=0;j<=i;j++)
            C[i][j] = ((j>=1? C[i-1][j-1] : 0) + C[i-1][j]) %mod;
    inv[1] = 1;
    for(int i=2;i<=N;i++) inv[i] = (ULL)(mod-mod/i)*inv[mod%i]%mod;
}
U quick_exp(U a,int p)
{
    if(!p) return 1;
    U tmp = quick_exp(a,p>>1);
    tmp = (ULL)tmp*tmp %mod;
    if(p&1) return (ULL)tmp*a%mod;
    else return tmp;
}
U geometric(U q,int n)
{
    if(q == 1) return n%mod;
    else return (ULL)q*inv[q-1]%mod*((quick_exp(q,n)+mod-1)%mod)%mod;
}
U work(int n,int k)
{
    U ret = 0;
    for(int i=0;i<k;i++)
    {
        U tmp = (ULL) C[k][i] * geometric(k-i,n) % mod;
        if(i&1) ret = (ret + mod - tmp) % mod;
        else (ret += tmp) %= mod;
    }
    return ret;
}
int main()
{
    freopen("pearl.in","r",stdin);
    freopen("pearl.out","w",stdout);
    init();
    int T; read(T);
    while(T--)
    {
        int n,k;
        read(n); read(k);
        U ans = work(n,k);
        printf("%u\n",ans);
    }
    return 0;
}
原创文章 152 获赞 15 访问量 6万+

猜你喜欢

转载自blog.csdn.net/ourfutr2330/article/details/53171421