训练赛Day7 - The 36th ACMICPC Asia Regional Dalian Site —— Onsite Contest

训练赛Day7 - The 36th ACM/ICPC Asia Regional Dalian Site —— Onsite Contest

D - Hexadecimal View

题目大意

十六进制对计算机程序员非常重要和有用。您被要求提供给定数据的十六进制视图。十六进制视图由一行或多行组成。除最后一行之外的每一行代表16个字符。每行由三个以空格分隔的列组成:

  • addr:此行的4位十六进制起始地址。
  • dump:此行的十六进制表示形式,用空格分隔每两个字符。如果最后一行中少于16个字符,请用空格填充它。
  • text:此行的ASCII转换,大写字符转换为小写和小写字符转换为大写。

使用小写字母数字。有关详细信息,请参阅示例。

数据范围

有多个测试用例。每行都是一个测试用例。该行由不少于1个且不超过4096个可打印字符组成,包括空格。

【样例解释】

  • 样例输入
Hex Dump
#include <cstdio>
printf("Hello, World!\n");
main = do getLine >>= print . sum . map read . words
  • 样例输出
0000: 4865 7820 4475 6d70                     hEX dUMP
0000: 2369 6e63 6c75 6465 203c 6373 7464 696f #INCLUDE <CSTDIO
0010: 3e                                      >
0000: 7072 696e 7466 2822 4865 6c6c 6f2c 2057 PRINTF("hELLO, w
0010: 6f72 6c64 215c 6e22 293b                ORLD!\N");
0000: 6d61 696e 203d 2064 6f20 6765 744c 696e MAIN = DO GETlIN
0010: 6520 3e3e 3d20 7072 696e 7420 2e20 7375 E >>= PRINT . SU
0020: 6d20 2e20 6d61 7020 7265 6164 202e 2077 M . MAP READ . W
0030: 6f72 6473                               ORDS

解题思路

暴力写就行。。。使用%x输出十六进制更方便。

AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<string>
using namespace std;
char s[5000];
string str;
int main() {
    while(gets(s)) {
        int len = strlen(s);
        int tot = 0, res = 0;
        str = " ";
        for(int i = 0; i < len; i++) {
            if(tot == 16) {
                cout << str << endl;
                str = " ";
                tot = 0;
                res += 16;
            }
            if(s[i] >= 'a' && s[i] <= 'z')str += (s[i] + ('A' - 'a'));
            else if(s[i] >= 'A' && s[i] <= 'Z')str += (s[i] + ('a' - 'A'));
            else str += s[i];
            if(tot == 0) {
                if(res < 16)printf("000");
                else if(res < 16 * 16)printf("00");
                else if(res < 16 * 16 * 16)printf("0");
                printf("%x:", res);
            }
            if(tot % 2 == 0)printf(" ");
            printf("%x", s[i]);
            tot++;
        }
        if(tot % 2 == 0 && tot != 16)printf(" ");
        for(int i = tot + 1; i <= 16; i++) {
            printf("  "); 
            if(i % 2 == 0 && i != 16)printf(" ");
        }
        if(tot != 0)cout << str << endl;
    }
    return 0;
}

E - Number String

题目大意

给你一个只包含字符’I’和’D’的字符串,’I’代表当前数字比后面数字值大,’D’代表当前数字比后面数字值小,问你满足该字符串条件的排列的个数%(109+7)。长度为len的串为len+1个数。

数据范围

字符串长度为1到1000,只包含’I’,’D’,’?’,’?’代表该位置可以是’I’也可以是’D’。

解题思路

首先状态为dp[i][j]代表前i个数以j结尾满足条件的方案数。

  • 即如果第i-1个字符是’I’,即代表第i个数字要比第i-1个数字大,假如第i个数字以j结尾,即dp[i][j]=k=1j1dp[i1][k]
  • 又如果第i-1个字符是’D’,即代表第i个数字要比第i-1个数字小,假如第i个数字以j结尾,即dp[i][j]=k=ji1dp[i1][k]。为什么k可以等于j呢,因为当前i-1个数字最后以j结尾的话,当我第i个数放j时,只要将前面所有大于等于j的数加一,就能保证又满足方案,并且由于前i-1个数最大值是i-1,所以即使加一,在用前i个数时也不会大于i。
  • 如果第i-1个字符是’?’,即为两种情况之和,即dp[i][j]=k=1i1dp[i1][k]

然后前缀和是可以维护的,其实用一维数组就行,我这写时没想太多。。。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL Pt = 1e9 + 7;
const int maxn = 1000;
LL dp[maxn + 5][maxn + 5];
LL sum[maxn + 5][maxn + 5];
char s[maxn + 5];
int main() {
    while(scanf("%s", s) != EOF) {
        int len = strlen(s);
        memset(dp, 0, sizeof(dp));
        memset(sum, 0, sizeof(dp));
        dp[1][1] = 1LL; sum[1][1] = 1LL, sum[1][0] = sum[0][0] = 0LL;
        for(int i = 2; i <= len + 1; i++) {
            for(int j = 1; j <= i; j++) {
                if(s[i - 2] == 'I')dp[i][j] = sum[i - 1][j - 1];
                else if(s[i - 2] == 'D')dp[i][j] = ((sum[i - 1][i - 1] - sum[i - 1][j - 1]) % Pt + Pt) % Pt;
                else if(s[i - 2] == '?')dp[i][j] = sum[i - 1][i - 1];
                sum[i][j] = (sum[i][j - 1] + dp[i][j]) % Pt;
            }
        }
        LL res = 0LL;
        for(int i = 1; i <= len + 1; i++)res = (res + dp[len + 1][i]) % Pt;
        printf("%lld\n", res);
    }
    return 0;
}

G - Rescue the Rabbit

题目大意

给你n个串,每个串都有一个价值,并让你构造一个只包含’A’,’C’,’G’,’T’四个字母的长度为l的字符串使得总价值最大。只要在字符串l中出现就算一次答案,并且每个串只记一次。

数据范围

1n10,1l100,1wi100

解题思路

这种题是想不了的,,,,,AC自动机+DP,首先得预处理出以每个结点结尾的取串状态,这个在处理AC自动机的fail指针和Insert时可以处理。然后dp[i][j][k]代表长度为i的串以trie树上第j个结点结尾状态为k的状态是否存在。然后转移的话,第j个结点可以转移到它的下一个结点,并且取串状态也会更新为新的结点加上当前父节点的状态。最后对于所有存在的状态取最大值,记得要滚动一下。。

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
using namespace std;
const int maxn = 2000;
char s[maxn + 5];
int A[15];
struct AC_Automaton {
    struct NOOD {
        int next[4];
        int fail, end;
    }node[maxn + 5];
    int size;
    bool dp[2][maxn + 5][maxn + 5];
    void Init() {
        for(int i = 0; i <= maxn; i++) {
            memset(node[i].next, -1, sizeof(node[i].next));
            node[i].fail = 0, node[i].end = 0;
        }
        size = 1;
    }
    int Getx(char c) {
        if(c == 'A')return 0;
        if(c == 'C')return 1;
        if(c == 'G')return 2;
        if(c == 'T')return 3;
    }
    void Insert(char s[], int id) {
        int n = strlen(s);
        int now = 0;
        for(int i = 0; i < n; i++) {
            int x = Getx(s[i]);
            if(node[now].next[x] == -1)node[now].next[x] = size++;
            now = node[now].next[x];
        }
        node[now].end |= (1 << (id - 1));
    }
    void Get_fail() {
        node[0].fail = -1;
        queue<int> que;
        for(int i = 0; i < 4; i++) {
            if(node[0].next[i] == -1)node[0].next[i] = 0;
            else {
                node[node[0].next[i]].fail = 0;
                que.push(node[0].next[i]);
            }
        }
        while(!que.empty()) {
            int u = que.front(); que.pop();
            node[u].end |= node[node[u].fail].end;
            for(int i = 0; i < 4; i++) {
                if(node[u].next[i] == -1)node[u].next[i] = node[node[u].fail].next[i];
                else {
                    node[node[u].next[i]].fail = node[node[u].fail].next[i];
                    que.push(node[u].next[i]);
                }
            }
        }
    }
    int Get(int x, int n) {
        int res = 0;
        for(int i = 1; i <= n; i++)
            if((1 << (i - 1)) & x)res += A[i];
        return res;
    }
    void solve(int n, int l) {
        memset(dp[0], 0, sizeof(dp[0]));
        int t = 0; dp[t][0][0] = 1;
        for(int i = 1; i <= l; i++) {
            memset(dp[t ^ 1], 0, sizeof(dp[t ^ 1]));
            for(int j = 0; j < size; j++) {
                for(int k = 0; k < (1 << n); k++) {
                    for(int x = 0; x < 4; x++) {
                        dp[t ^ 1][node[j].next[x]][k | node[node[j].next[x]].end] |= dp[t][j][k];
                    }
                }
            }
            t ^= 1;
        }
        int Max = -1;
        for(int i = 0; i < size; i++)
            for(int j = 0; j < (1 << n); j++)
                if(dp[t][i][j])Max = max(Max, Get(j, n));
        if(Max < 0)printf("No Rabbit after 2012!\n");
        else printf("%d\n", Max);
    }
}AC;

int main() {
    int n, l;
    while(~scanf("%d%d", &n, &l)) {
        AC.Init();
        for(int i = 1; i <= n; i++) {
            scanf("%s %d", s, &A[i]);
            AC.Insert(s, i);
        }
        AC.Get_fail();
        AC.solve(n, l);
    }
    return 0;
}

I - The Boss on Mars

题目大意

给定一个数n,找出所有与n互质的数的四次方的和。

数据范围

T组数据,T1000,1n108

解题思路

对于一个数n找出与它互质的数不是很好找,但是找与它有公因子的数还是比较好找的,找出这些数然后用总和减掉就行。但是并不是一下全部找出来减完,而是要用到容斥原理,如果将一个数分成质因子组成的话,比如30 = 2 * 3 * 5;然后要找到所有与它不互质的数,就是要每个质因子即质因子的倍数,这里要减掉所有2的倍数的四次方,再减掉3倍数的四次方,再减掉5倍数的四次方,之后因为会减掉重复的数,比如同时是2和3的倍数,同时是2和5的倍数,同时是3和5的倍数,所以要再加上这些倍数的四次方,然后又会多加,因此又需要再次减掉2和3和5的倍数。就比如要求集合ABC,即可求A+B+C(AB+AC+BC)+ABC。之后求前n项四次方的和需要用到一个公式:

i=1ni4=n(n+1)(2n+1)(3n2+3n1)/30

若r是n的因子,要求r所有倍数的的因子可以求r4i=1n/ri4

AC代码

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;
const LL Pt = 1e9 + 7;
LL n;
vector<LL>ans;
LL qwe(LL x, LL y) {
    LL t = 1LL;
    while(y > 0LL) {
        if(y & 1)t = t * x % Pt;
        y /= 2LL;
        x = x * x % Pt;
    }
    return t;
}
LL Getsum(LL x) {
    return ((x * (x + 1) % Pt * (2 * x + 1) % Pt * (3 * x * x % Pt + 3 * x - 1) % Pt) % Pt + Pt) % Pt * qwe(30, Pt - 2) % Pt;
}
int main() {
    int T; scanf("%d", &T);
    while(T--) {
        ans.clear();
        scanf("%lld", &n);
        LL x = n;
        for(LL i = 2LL; i <= sqrt(n); i++) {
            if(x % i == 0LL) {
                while(x % i == 0)x /= i;
                ans.push_back(i);
            }
        }
        if(x > 1LL)ans.push_back(x);
        int size = ans.size();
        LL res = 0LL;
        for(int i = 0; i < (1 << size); i++) {
            int tot = 0; LL tmp = 1LL;
            for(int j = 0; j < ans.size(); j++) {
                if((1 << j) & i) {
                    tmp = tmp * ans[j] % Pt;
                    tot++;
                }
            }
            LL p = tmp;
            tmp = ((tmp * tmp % Pt) * tmp % Pt) * tmp % Pt;
            if(tot & 1)res = ((res - tmp * Getsum(n / p)) % Pt + Pt) % Pt;
            else res = (res + tmp * Getsum(n / p) % Pt) % Pt;
        }
        printf("%lld\n", res);
    }
    return 0;
}
posted @ 2018-07-27 14:19  呵呵!!!  阅读(102)  评论(0编辑  收藏  举报