zoukankan      html  css  js  c++  java
  • 山西NOIp四校联考部分解题报告


    D1T1 Color

    题目描述

    一个1*n的方格图形 (不可旋转),用m种颜色填涂每个方格。每个方格只能填涂一种颜色,如果有相邻的方格颜色相同则视为可行方案,问有多少种可行方案。.

    输入格式:

    两个正整数:m n

    输出格式:

    一个数:可能的填涂方式总数,mod100003。

    输入样例:

    2 3

    输出样例:

    6

    样例解释:

    6种可能的填涂方式:

    0,0,0;   0,0,1;   0,1,1;  1,0,0;   1,1,0;   1,1,1
    • 对于10%的数据:m=2,n<33
    • 对于另外50%的数据:1<m<10000,1<n<=106
    • 对于100%的数据:1<=m<=108,1<=n<=1012

    分析

    1. 20分做法:暴力搜索。考场上对于数学题的一般方法。
    2. 其他思路:暴力打表找规律法。
    3. NB方法:排列组合【我不会有木有】。

    首先利用暴力搜索打一张表,包含了n = 1..8, m = 1..8的答案。然后开始找规律吧。至于怎么找,那就是仁者见仁智者见智了。我首先设f(n)为n个方格m种颜色的可行方案,g(n)为不可行方案。显然有g(n)=mnf(n)。然后用某种灵感构造一个数列h(n)=m×h(n1)+mn1h(n1)。对照表,发现总有f(n)=h(n)(m1)n1

    h(0)=0h(1)=1h(2)=2m1h(3)=3m23m+1h(4)=4m36m2+4m1

    发现了半个杨辉三角,根据二项式定理,猜想h(n)=mn(m1)2,用归纳法:

    h(0)=m0(m1)0=0h(n)=m×h(n1)+mn1h(n1)=[mn1(m1)n1](m1)+mn1=mn1(m1+1)(m1)n=mn(m1)n

    原式得证。因此,f(n)=h(n)(m1)n1=mn(m1)n(m1)n1。利用快速幂便可以AC。

    为什么要描述这种方法呢?注意,考信息不是考数学,计算机不是白给你的。要充分利用计算机暴力快的特点,结合一些奇技淫巧解决问题!代码略。

    D1T2 Rigel

    题目背景

    南欧包括:国家1:罗马尼亚、国家2:保加利亚、国家3:塞尔维亚、国家4:马其顿、国家5:阿尔巴尼亚、国家6:希腊、国家7:斯洛文尼亚、国家8:克罗地亚、国家9:波斯尼亚……。一个名叫Rigel的oier受学校之托乘坐火车通往各个国家处理CCF相关事情。。

    题目描述

    Rigel决定通过一条铁路线去往各个国家,铁路依此连接国家1,国家2…国家n。该铁路经过n个国家,每个国家都有一个车站。经过两国之间必须购买车票,第k段铁路连接了国家k和国家k+1。为推广本国运输业,每个国家除了发放单次车票Ak。也发放IU卡,使用IU卡的好处是多次往来于该国家可以节约费用,一次乘车费是BkBk<Ak,但是该卡有工本费Ck.(Iu卡可以无限充值).

    Rigel要去m个国家,从城市R1开始分别按照R1,R2,R3…Rm的顺序前往各个国家,可能会多次到达一个国家。相邻访问的国家位置不一定相邻,且不会是同一个国家。

    他想知道这一趟处理事务下来,至少会花掉多少的钱,包括购买单次车票、买IU卡和充值的总费用。

    输入格式:

    第一行两个整数,n,m。
    接下来一行,m个数字,表示Ri
    接下来n-1行,表示第k段铁路的Ak,Bk,Ck

    输出格式:

    一个整数,表示最少花费

    输入样例:

    9 10
    3 1 4 1 5 9 2 6 5 3
    200 100 50
    300 299 100
    500 200 500
    345 234 123
    100 50 100
    600 100 1
    450 400 80
    2 1 10

    输出样例:

    6394

    说明

    2-3和8-9为单次车票,其余都是买卡充值。
    - 对于30%数据m=2
    - 对于另外30%数据 n<=1000,m<=1000
    - 对于100%的数据 m,n<=100000,其中Ak,Bk,Ck<=100000

    分析

    就是luogu月赛的海底高速嘛。由于是离线处理,容易想到用差分数组。一个差分上去,前缀累加,再判断每一段怎么样便宜即可。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    long long n, m;
    long long c[100005];
    long long a[100005];
    int main()
    {
        freopen("input.txt", "r", stdin);
        freopen("output.txt", "w", stdout);
        memset(c, 0, sizeof c);
        scanf("%lld%lld", &n, &m);
        for (int i = 1; i <= m; i++) 
            scanf("%lld", &a[i]);
        for (int i = 1; i < m; i++) {
            long long from = a[i], to = a[i+1];
            if (from > to) swap(from, to);
            c[from]++;
            c[to]--;
        }
        long long sum = 0;
        long long sigma = 0;
        for (int i = 1; i < n; i++) {
            sum += c[i];
            long long ai, bi, ci;
            //printf("%d ", sum);
            scanf("%lld%lld%lld", &ai, &bi, &ci);
            if (ai*sum <= sum*bi+ci) sigma += ai*sum;
            else sigma += sum*bi+ci;
        }
        printf("%lld
    ", sigma);
        return 0;
    }

    D2T1 Area

    题目描述

    小康同学在纸上画了N个平行于坐标轴的矩形,这N个矩形可能有互相覆盖的部分,他想知道这些矩形的总面积是多少。注意,互相覆盖的部分只能算一次。.

    输入格式:

    输入第一行为一个数N(1≤N≤100),表示矩形的数量。
    下面N行,每行四个整数,分别表示每个矩形的左下角和右上角的坐标,坐标范围为–10^8到10^8之间的整数。

    输出格式:

    输出只有一行,一个整数,表示图形的面积。

    输入样例:

    3
    1 1 4 3
    2 -1 3 2
    4 0 5 2

    输出样例:

    10

    数据范围

    • 对于40%的数据:n<=40,坐标绝对值<1000
    • 对于100%的数据:n<=100,坐标绝对值<10^8

    分析

    首先bs这题超纲。。

    1. 40分做法:二维差分数组前缀和累加。毫无脑水。
    2. 100分做法:离散化或漂浮法。漂浮法更好些,于是用漂浮。

    漂浮法是计算几何中计算重叠矩形面积的方法。具体来说,就是把矩形放入水中,分为n层,每层一个。从最底端的开始上浮,碰到上面的矩阵就裂开成若干块,直到上浮到顶,就是要求的面积。可以看成你在水面向下俯视,所能看到的就是要浮上来的,也就是总面积。

    一个trick:判断矩形相交。

    以下内容引用自http://blog.csdn.net/cxf7394373/article/details/7535105

    假定矩形是用一对点表达的(minx,miny)(maxx,   maxy),那么两个矩形rect1{(minx1,miny1)(maxx1,   maxy1)},  rect2{(minx2,miny2)(maxx2,   maxy2)} 相交的结果一定是个矩形,构成这个相交矩形rect{(minx,miny)(maxx, maxy)}的点对坐标是:   
        minx   =   max(minx1,   minx2)   
        miny   =   max(miny1,   miny2)   
        maxx   =   min(maxx1,   maxx2)   
        maxy   =   min(maxy1,   maxy2)   
    如果两个矩形不相交,那么计算得到的点对坐标必然满足   
      minx   >   maxx   或者     miny   >   maxy   

    由于中间有几个步骤没想清楚,分类比较多。代码冗长。。

    #include <iostream>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    using namespace std;
    
    struct obj {
            struct point {
                    long long x, y;
                    point(){}
                    point(long long a, long long b) : x(a), y(b) {}
            }; 
            point left_bot, right_top;
            obj() {}
            obj(point a, point b):left_bot(a), right_top(b) {}
            obj(long long a, long long b, long long c, long long d)
            {
                    *this = obj(point(a, b), point(c, d));
            }
            operator long long() const 
            {
                    return (right_top.x-left_bot.x)*(right_top.y-left_bot.y);
            }
    }arr[105];
    long long ans = 0;
    
    inline bool fail(long long a, long long b, long long c, long long d)
    {
            return a >= c || b >= d;
    }
    
    void lift_up(obj rect, int i)
    {
            if (i == 0) {
                    ans += rect;
                    return;
            }
            obj ths = arr[i];
            long long minx = max(ths.left_bot.x, rect.left_bot.x);
            long long miny = max(ths.left_bot.y, rect.left_bot.y);
            long long maxx = min(ths.right_top.x, rect.right_top.x);
            long long maxy = min(ths.right_top.y, rect.right_top.y);
            //printf("%lld %lld %lld %lld
    --
    ", minx, miny, maxx, maxy);
            if (fail(minx, miny, maxx, maxy))
                    lift_up(rect, i-1);
            else {
                    long long a = rect.left_bot.x, b = rect.left_bot.y, c = rect.right_top.x, d = rect.right_top.y;
                    long long e = minx, f = miny, g = maxx, h = maxy;
                    if (!fail(a, b, e, d))
                            lift_up(obj(a, b, e, d), i-1);
                    if (!fail(e, b, g, f))
                            lift_up(obj(e, b, g, f), i-1);
                    if (!fail(e, h, g, d))
                            lift_up(obj(e, h, g, d), i-1);
                    if (!fail(g, b, c, d))
                            lift_up(obj(g, b, c, d), i-1);
            }
    }
    
    int main()
    {
            freopen("input.txt", "r", stdin);
            freopen("output.txt", "w", stdout);
            ios::sync_with_stdio(false);
            int n;
            cin >> n;
            for (int i = 1; i <= n; i++) {
                    long long a, b, c, d;
                    cin >> a >> b >> c >> d;
                    if (fail(a, b, c, d))
                            arr[i] = obj(0, 0, 0, 0);
                    else
                            arr[i] = obj(a, b, c, d);
            }
            for (int i = n; i >= 1; i--)
                    lift_up(arr[i], i-1);
            cout << ans << endl;
            return 0;
    }

    D2T3 review

    题目描述

    转眼间又到期中考试了,小康同学开始了紧张的复习。教材中总共有N个要复习的知识点,有些知识点之间还存在着单向联系,例如知识点A→B,只要掌握了前置的知识点A,知识点B也能顺利掌握。小康给每个知识点进行了评估,并标出它的相应分值。只要掌握了这个知识点,就获得相应的分数。小康同学现在要选择K个知识点开始复习,使得可以获得最大的分值。

    输入描述

    第一行:N K (分别表示知识点的个数和选择的知识点)
    以下N行,每行两个整数a和b
    (a表示这个知识点单向连接的下一个知识点,如果a=0,表示这个知识点没有单向连接的下一个知识点,b表示这个知识点的分值)

    输出描述

    仅一个数,表示最大得分。

    样例输入

    8 2
    0 1
    1 1
    2 100
    2 100
    0 1
    5 1
    6 2
    6 2

    样例输出

    202

    数据范围

    • 对于30%的数据:n<=1000,k<=30
    • 对于60%的数据:n<=50000,k<=100
    • 对于100%的数据:n<=200000,k<=500,1<=b<=1000000

    分析

    首先说明这个数据弱了:没有环。事实上,题目描述根本没有排除有环的情况,例如:我学了冒泡,可以轻易掌握选择;学了选择,也可以轻易掌握冒泡。这就形成了一个环。所以,标程是有漏洞的!

    首先看到数据规模,应该不难想到缩点建图。由于每个点的出度都为0或1,因此,在同一个连通分量中,不会存在两个或以上的环【同《Noip2015 信息传递》】,且任意一个环在缩点后是没有出边的。利用kosaraju缩点建图(比常规要简单一些),不难证明,得到图的转置图必然是森林!。那么必然要选叶子。怎么选呢?贪心!考虑任意两个选择,如果两个选择位于同一个连通分量内,则先后无所谓;如果不是,那先选择结果较大的不可能差于先选结果较小的。因此只需要每次选结果最大的,然后修改其他值【选择这个叶子会把其祖先一起选择,因此会影响到同一连通分量内的其他叶子】,重复选择k次即可。这里用线段树实现。
    代码过于坑,考场上一不小心就爆0了。改了一句就AC……

    #include <iostream>
    #include <cstdio>
    #include <cctype>
    #include <stack>
    #include <cstring>
    #include <queue>
    using namespace std;
    
    #define maxn (1<<21)
    typedef long long ll;
    #define lc (k<<1)
    #define rc ((k<<1)+1)
    
    struct zkw_heap {
            ll dat[1<<22];
            int from[1<<22];
            zkw_heap()
            {
                    memset(dat, 0, sizeof dat);
                    for (int i = maxn; i <= maxn+maxn-1; i++)
                            from[i] = i-maxn+1;
            }
            void modify(int i, ll j)
            {
                    int p = i+maxn-1;
                    dat[p] = j;
                    for (int k = p>>1; k; k>>=1) 
                            if (dat[lc] >= dat[rc]) {
                                    dat[k] = dat[lc];
                                    from[k] = from[lc];
                            } else {
                                    dat[k] = dat[rc];
                                    from[k] = from[rc];
                            }
                   // cout << "--tree " << from[1] << " " << dat[1] << "--
    ";
            }
    }zkw;
    
    ll read() 
    {
            ll a = 0;
            int c;
            do c = getchar(); while(!isdigit(c));
            while (isdigit(c)) {
                    a = a*10+c-'0';
                    c = getchar();
            }
            return a;
    }
    
    struct graph {
            struct p {
                    int to, next;
            }edge[200005];
            int head[200005], top;
            graph() 
            {
                    top = 0;
                    memset(head, 0, sizeof head);
            }
            void push(int i, int j)
            {
                    edge[++top].to = j;
                    edge[top].next = head[i];
                    head[i] = top;
            }
    } anti, new_anti;
    
    int normal[200005], dealed[200005];
    int dfn[200005], top = 0;
    int group[200005];
    int vis[200005];
    int rd[200005], anti_leave[200005];
    ll ranks[200005];
    ll rk[200005];
    int n, gp = 0, k;
    void get_dfn(int i)
    {
            vis[i] = 1;
            if (!vis[normal[i]] && normal[i] != 0)
                    get_dfn(normal[i]);
            dfn[++top] = i;
            //cout << "dfn[" << top << "] = " << i << endl; 
    }
    void dfs(int i, int gp)
    {
            vis[i] = 1;
            group[i] = gp;
            //cout << i << " in group " << gp << endl;
            rk[gp] += ranks[i];
            for (int k = anti.head[i]; k; k = anti.edge[k].next) {
                    int to = anti.edge[k].to;
                    if (!vis[to] && to != 0)
                            dfs(to, gp);
            }
    }
    
    int leaves[200005], num = 0;
    long long dp[200005];
    
    long long get_rank(int i)
    {
            if (dp[i] != -1) return dp[i];
            if (i == 0) return 0;
            return dp[i] = get_rank(dealed[i]) + rk[i];
    }
    
    queue<int> que;
    void change(int i)
    {
           // cout << "changing " << i << endl;
            vis[i] = 1;
            if (dealed[i] && !vis[dealed[i]])
                    change(dealed[i]);
            while (!que.empty()) que.pop();
            que.push(i);
            while (!que.empty()) {
                    int k = que.front(); que.pop();
                    if (rd[k] == 0) {
                            zkw.modify(anti_leave[k], dp[k] - dp[i]);
                            //dp[k] -= dp[i];
                    }
                    for (int p = new_anti.head[k]; p; p = new_anti.edge[p].next) 
                            if (!vis[new_anti.edge[p].to])
                                    que.push(new_anti.edge[p].to);
            }
    }
    
    int main()
    {
            freopen("input.txt", "r", stdin);
            freopen("output.txt", "w", stdout);
            memset(vis, 0, sizeof vis);
            memset(dealed, 0, sizeof dealed);
            memset(anti_leave, 0, sizeof anti_leave);
            memset(dp, -1, sizeof dp);
            memset(rd, 0, sizeof rd);
            n = read(); k = read();
            for (int i = 1; i <= n; i++) {
                    normal[i] = read();
                    ranks[i] = read();
                    if (normal[i])
                            anti.push(normal[i], i);
            }
            for (int i = 1; i <= n; i++)
                    if (!vis[i])
                            get_dfn(i);
            memset(vis, 0, sizeof vis);
            for (int i = n; i >= 1; i--)
                    if (!vis[dfn[i]])
                            dfs(dfn[i], ++gp);
            for (int i = 1; i <= n; i++)
                    if (group[normal[i]] != group[i]) {
                            dealed[group[i]] = group[normal[i]];
                            new_anti.push(group[normal[i]], group[i]);
                            rd[group[normal[i]]]++;
                    }
           // for (int i = 1; i <= gp; i++)
           //         cout << rk[i] << " ";
           // puts("");
            // 缩点建图
            for (int i = 1; i <= gp; i++)
                    if (rd[i] == 0) {
                            leaves[++num] = i;
                            anti_leave[i] = num;
                            //cout << i << endl;
                    }
            memset(vis, 0, sizeof vis);
            for (int i = 1; i <= num; i++) {
                    zkw.modify(i, get_rank(leaves[i]));
                    //cout << leaves[i] << " " << get_rank(leaves[i]) << endl;
            }
            /*for (int i = 1; i <= gp; i++)
                    cout << dp[i] << " ";
            puts("");*/
            long long ans = 0;
            for (int i = 1; i <= k; i++) {
                    //cout << zkw.from[1] << endl;
                    int lvs = leaves[zkw.from[1]];
                    //vis[lvs] = 1;
                    ans += zkw.dat[1];
                   // cout << lvs << " " << zkw.dat[1] << endl;
                    int mark = zkw.from[1];
                    if (!vis[dealed[lvs]] && dealed[lvs])
                            change(dealed[lvs]);
                    zkw.modify(mark, 0);
                   /* for (int i = 1; i <= gp; i++)
                            cout << dp[i] << " ";
                    puts("");*/
            }
            cout << ans << endl;
            return 0;
    }
  • 相关阅读:
    运行期优化
    虚拟机字节码执行引擎
    虚拟机类加载机制
    类文件结构
    垃圾收集机制
    浅析Spring MVC工作机制
    TomCat系统架构
    docker安装mongo初体验
    微服务笔记--概念
    使用maven构建多模块项目_记录
  • 原文地址:https://www.cnblogs.com/ljt12138/p/6684363.html
Copyright © 2011-2022 走看看