zoukankan      html  css  js  c++  java
  • SXOI2016 部分解题报告

    第四题还没做…先贴前三个题的。

    T1 bridge

    Description

    有一支n个人的队伍要过一座限重为W的桥,我们规定这支队伍过桥时只能分组过,即当一组全部过去后,下一组才能接着过。给定每个队员的重量wi和过桥时间ti,一组人的过桥时间为花费时间最多的人的时间。问如何分组使得总的过桥时间最短。

    Input

    • 第一行为两个正整数W,n
    • 接下来n行每行两个数ti,wi

    Output

    • 仅一个数,为最小花费时间。

    Sample

    Input

    100 3
    24 60
    10 40
    18 50

    Output

    42

    Hint

    • 对于40%的数据,n8
    • 对于70%的数据,n12
    • 对于100%的数据,n16,100W400,1t50,10w100

    Solution

    数据范围告诉我们应该考虑状压dp。

    dp[S] 表示S中的人还未过河所用的时间,则有:

    dp[S]=max{dp[SA]+T(A)|AS,W(A)W}

    T(A),W(A)分别表示通过的时间最大值和重量总和。

    现在问题转化为如何枚举集合的子集。如果用朴素的枚举只能拿到70分左右。从《挑战程序设计竞赛》上学到的一个方法则可以降低复杂度:

        sub = S;
        do {
            //...
            sub = (sub-1)&S;
        } while (sub > 1)

    这样处理sub就枚举了2S

    总的复杂度可以用:

    1in(ni)2i

    打个表就知道能O(能过)…

    #include <bits/stdc++.h>
    using namespace std;
    
    int dp[1<<18], W, n;
    int w[20], t[20];
    int A[1<<18], M[1<<18];
    
    int dfs(int S)
    {
        if (S == 1) return 0;
        if (dp[S] != -1) return dp[S];
        dp[S] = 1e6;
        int sap = S;
        do {
            int ans = A[sap], mx = M[sap];
            if (ans <= W)
                dp[S] = min(dp[S], dfs(S^sap)+mx);
            sap = S&(sap-1);
        } while (sap > 1);
        //cout << S << " --> " << dp[S] << endl;
        return dp[S];
    }
    
    int main()
    {
        freopen("bridge.in", "r", stdin);
        freopen("bridge.out", "w", stdout);
        memset(dp, -1, sizeof dp);
        scanf("%d%d", &W, &n);
        for (int i = 1; i <= n; i++)
            scanf("%d%d", &t[i], &w[i]);
        int S0 = (1<<(n+1))-1;
        for (int i = 2; i <= S0; i++) {
            for (int j = 1; j <= n; j++)
                if (i&(1<<j))
                    A[i] += w[j], M[i] = max(M[i], t[j]);
        }
        cout << dfs(S0) << endl;
        return 0;
    }

    T2 sequence

    Description

    定义F为斐波那契数列,被定义为:

    F0=0,F1=1Fn=Fn1+Fn2,n>1

    G为一个序列,定义为:

    Gi=a1+a2+...+am=i,ak>0jmFaj

    对于给定的n,求Gnmod109+7

    Input

    • 一个数n

    Output

    • 一个数Gnmod109+7

    Sample

    Input

    3

    Output

    5

    Hint

    3=1+1+1  F1×F1×F1=1=2+1  F2×F1=1=1+2  F1×F2=1=3  F3=2

    1+1+1+2=5

    • 对于30%的数据,n20
    • 对于50%的数据,n5000
    • 对于70%的数据,n107
    • 对于100%的数据,n1018

    Solution

    首先看样例解释很容易发现一个递推式:

    Gn=1inFi×Gni

    打表找规律会发现 i3,Gi=2Gi1+Gn2。现在来证明一下:首先可以证明G1=1,G2=2G1+G2=G3+1,然后用归纳法

    2Gn1+Gn2=1in12FiGn1i+1in2FiGn2i=1in3Fi(2Gn1i+Gn2i)+2Fn1G0+2Fn2G1+Fn2G0=1in3FiGni+2Fn1+3Fn2  (1)

    2Fn1+3Fn2=2Fn2+Fn1+Fn=n2inFiGni

    因此:

    (1)=Gn

    原命题得证。然后只需要矩阵

    [2110]

    做快速幂取模就好了。注意特判。

    #include <bits/stdc++.h>
    using namespace std;
    
    struct Matrix {
        long long a[3][3];
        Matrix()
        { memset(a, 0, sizeof a); }
        friend Matrix operator * (const Matrix &a, const Matrix &b)
        {
            Matrix C;
            for (int i = 1; i <= 2; i++)
                for (int j = 1; j <= 2; j++)
                    for (int k = 1; k <= 2; k++)
                        (C.a[i][j] += a.a[i][k] * b.a[k][j]) %= 1000000007;
            return C;
        }
    };
    
    Matrix I()
    {
        Matrix C;
        memset(C.a, 0, sizeof C.a);
        C.a[1][1] = C.a[2][2] = 1;
        return C;
    }
    
    Matrix power(const Matrix &a, long long n)
    {
        if (n == 0) return I();
        Matrix p = power(a, n>>1);
        p = p*p;
        if (n&1) p = p*a;
        return p;
    }
    
    int main()
    {
        freopen("sequence.in", "r", stdin);
        freopen("sequence.out", "w", stdout);
        long long n;
        cin >> n;
        if (n == 0) {cout << 1 << endl; return 0;}
        if (n == 1) {cout << 1 << endl; return 0;}
        Matrix M; M.a[1][1] = 2, M.a[1][2] = 1, M.a[2][1] = 1;
        M = power(M, n-1);
        cout << M.a[1][1] << endl;
        return 0;
    }

    T3 string

    Description

    给定n个回文串,s1,s2,...,sn。求有多少有序整数对 (i,j) 使得sisj 仍为回文串。

    Input

    • 第一行一个整数n
    • 接下来n行,每行先是一个整数w,表示这个字符串的长度;然后是一个空格;之后给出这个长度为w的字符串。保证w0

    Output

    • 仅一个正整数,表示有序对的个数。

    Sample

    Input

    7
    2 aa
    3 aba
    3 aaa
    6 abaaba
    5 aaaaa
    4 abba

    Output

    14

    Hint

    • 14个有序对中,6个为 (i,i),其他8个分别为:
    • (1,3),(1,5),(3,5),(2,6),(3,1),(5,1),(5,3),(6,2)

    • m为总字符数。

    • 对于20%的数据,n100,m500
    • 对于40%的数据,n5000,m2000000
    • 对于100%的数据,n2000000,m2000000

    Solution

    当时考场上sdf一个女选手怒而碾过Orz……

    我们用S1 表示S的反串。容易知道“穿脱原则”: (ab)1=b1a1 在这里成立。

    考虑两个回文串a,b,根据定义有:

    a=a1,b=b1ba=b1a1

    满足题目中条件为:

    ab=(ab)1

    按照穿脱原则展开:

    ab=b1a1

    也就是:

    ab=ba

    所以原问题转化为了求ab=ba的对数。考虑用多项式哈希:

    hash(ab)=hash(a)×base|b|+hash(b)hash(ba)=hash(b)×base|a|+hash(a)

    移项并整理,得到:

    hash(a)base|a|1=hash(b)base|b|1

    可以看到两侧都变成了只含有一个量的表达式,成功地将两个量的关系转化成了一个量的性质,除法可以用模大素数下的乘法逆元处理,因此就可以丢进hash算了。

    然而一个问题是这样做冲突严重。如果懒得写拉链怎么办呢?一个有趣的方法是考虑ab=ba必然有a[|a|]=b[|b|],而且他们是最高位,计算hash的时候乘了一个巨大的数。因此我们可以把hash值乘上这一位的ascii,就可以很大程度避免冲突了。

    至于base的选取..亲测37效果最好…不知道为什么,可能是数据的缘故吧…

    #include <bits/stdc++.h>
    using namespace std;
    
    long long mod = 2016122203ll, base = 37;
    long long power(long long a, long long n)
    {
        if (n == 0) return 1;
        long long p = power(a, n>>1);
        (p *= p) %= mod;
        if (n&1) (p *= a) %= mod;
        return p;
    }
    long long inv(long long a)
    { return power(a, mod-2); }
    long long hash_val(char str[])
    {
        long long ans = 0;
        for (char *p = str; *p != ''; ++p)
            ((ans *= base) += (*p-'a'+1)) %= mod;
        return ans;
    }
    
    map<long long, int> hash_set;
    int n, w;
    char str[2000005];
    long long cnt = 0;
    
    inline int read() {
        int a = 0, c;
        do c = getchar(); while(!isdigit(c));
        while (isdigit(c)) {
            a = a*10+c-'0';
            c = getchar();
        }
        return a;
    }
    
    inline void read(char str[])
    {
        char c;
        int i = 0;
        do c = getchar(); while(!isalpha(c));
        while (isalpha(c)) {
            str[i++] = c;
            c = getchar();
        }
        str[i] = '';
    }
    
    int main()
    {
        freopen("string.in", "r", stdin);
        freopen("string.out", "w", stdout);
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            w = read();
            read(str);
            long long val = inv(((power(base, w)-1)+mod)%mod);
            (val *= hash_val(str)) %= mod;
            cnt += hash_set[val*str[w-1]];
            hash_set[val*str[w-1]]++;
        }
        cout << cnt*2+n << endl;
        return 0;
    }
  • 相关阅读:
    移动端rem布局的适配mixin【转藏】
    移动端布局Demo展示图文
    百思不得其解—这些年做Web开发遇到的坑?
    elemetnui 分页..解决 bug
    linq.js
    yalinqo 的使用...
    vue 利用 v-model 实现 双向传递数据..
    Mui 选项卡 tab 高度 没有自适应....
    css flex 使内容 水平居中 的方法...
    IDEA 在 专注模式下 显示 行号 和 缩进线...
  • 原文地址:https://www.cnblogs.com/ljt12138/p/6684331.html
Copyright © 2011-2022 走看看