2016-2017 ACM-ICPC (SEERC 2016) 【solved:7 / 11】

来源:互联网 发布:电脑怎么用手机网络 编辑:程序博客网 时间:2024/06/11 18:15

剩下的都没题解了。可做一点的怕是只有A和J了。
-update : 2017年10月9日21:43:43

C - Castle(kmp的next数组的应用)

题意:给出原串s,然后有3种操作,1、在s的末尾加上一个小写字母ch;2、把s的拷贝放入set;3、询问在set中的字符串是当前字符串s‘的后缀的个数。

思路:
  我们考虑到kmp中,next[i]的意义是,i这个前缀串中。它自己的前缀和自己的后缀的最长公共部分。
  首先毫无疑问我们要维护整个字符串的next数组。然后我们去dp,每次加入一个新的字符s[i]的时候,显然可以得知目前ans[i] = ans[pre[i]] 。然后如果再把这个前缀i第一次插到集合set里面的时候,显然ans[i]++,且只会加一次,因为是个set。需要打个vis标记一下。就可以得到答案了

#include <bits/stdc++.h>using namespace std;const int maxn = 2000000 + 5;char s[maxn];int pre[maxn], ans[maxn], vis[maxn];int main(){    int n, E;    scanf("%d%d", &n, &E);    getchar();    gets(s + 1);    int j = 0;    for(int i = 2; i <= n; i++)    {        while(j && s[j + 1] != s[i])    j = pre[j];        if(s[j + 1] == s[i])    j++;        pre[i] = j;    }    int idx = n;    while(E--)    {        int op;        scanf("%d", &op);        if(op == 1)        {            scanf(" %c", &s[++idx]);            while(j && s[j + 1] != s[idx])    j = pre[j];            if(s[j + 1] == s[idx])    j++;            pre[idx] = j;            ans[idx] = ans[j];        }        else if(op == 2)        {            if(vis[idx] == 0)   ans[idx]++, vis[idx] = 1;        }        else if(op == 3)        {            printf("%d\n", ans[idx]);        }    }    return 0;}

D - Reading Digits (简单模拟)

题意:有一种加密方式:1211->111221->312211。现在告诉你加密k次后的字符串,问你原串的第pos位是什么数字。(k40pos1e5

#include <bits/stdc++.h>using namespace std;int main(){    int k, pos;    scanf("%d%d", &k, &pos);    string s;    cin >> s;    while(k--)    {        string temp = "";        for(int i = 0; i < s.size(); i += 2)        {            int times = s[i] - '0';            while(times--)    temp += s[i + 1];        }        s = temp;    }    printf("%c\n", s[pos]);    return 0;}

F - Letters (小模拟进制转换)

题意:有一个ABCXYZAAABACAXAYAZ的序列,现在问你第n项是什么字符。例如第0项是A, 第25项是Z。

思路:A-Z:长度为1,有26个。AA-ZZ:长度为2,有26*26个。所以容易求出第n个字符所属于的那部分长度是len。然后会令group = delta/len,会得到你要找的数字属于哪个串,比如“AAA”或者是“DEF”。然后你发现就是一个你要找的那个串就是group = delta/len这个数字的值的26进制表示方式。然后需要倒过来找一下即可。

思路:

#include <bits/stdc++.h>using namespace std;long long r26[10];int main(){    r26[0] = 1;    for(int i = 1; i <= 7; i++) r26[i] = r26[i - 1] * 26;    int idx;    while(~scanf("%d", &idx))    {        long long num = 0, len = 1;        while(1)        {            long long cur = 1LL * len * r26[len];            if(num + cur <= idx)            {                num += cur;            }            else break;            len++;        }        vector<int>ans;        long long group = (idx - num) / len;        long long id = (idx - num) % len;        for(int i = 0, temp = group; i < len; i++)        {            ans.push_back(temp % 26);            temp /= 26;        }        printf("%c\n", ans[len-1-id] + 'A');    }    return 0;}

G - Pokemons (water)

题意:告诉你n天,每天货物的价格,你一开始拥有的金钱。你要买exactly一次货物。问你最后最多获利多少(可能为负数)

思路:除了卡内存,卡时限以外。就是个水题。

#include <bits/stdc++.h>using namespace std;int readInt() {    int ret = 0;    while(1) {        char c = getchar();        if(c == ' ' || c == '\n') break;        if('0' > c || c > '9') continue;        ret *= 10;        ret += c - '0';    }    return ret;}double readFloat() {    double ret = 0;    double po = 1;    bool nega = false;    while(1) {        char c = getchar();        if(c == ' ' || c == '\n') break;        if(c == '-') {            nega = true;            continue;        }        if(c == '.') {            po /= 10;            continue;        }        if('0' > c || c > '9') continue;        if(abs(po - 1) > 1e-9) {            ret += po * (c - '0');            po /= 10;        }        else {            ret *= 10;            ret += c - '0';        }    }    return nega? -ret : ret;}int main(){    double st = readFloat();    int n = readInt();    double minPrice = readFloat();    double ans = 0;    for(int i = 1; i < n; i++)    {        double cur = readFloat();        if(i == 1)  ans = (cur - minPrice) * (st / minPrice);        if(cur < minPrice)  minPrice = cur;        else ans = max(ans, (cur - minPrice) * (st / minPrice));    }    printf("%.2f\n", ans);    return 0;}

H - Pub crawl (凸包)

题意:给出若干个平面上的点,保证无三点共线,你每次都只能连上左边的点,问你最多能连几个点,给出连线顺序。(N5000

思路:其实就是个不断做凸包的过程,把做过的点打个vis标记就行了。
关于复杂度的计算:《挑战》P261 3.6.4有个结论,凸包上的点是n级别的,所以复杂度是Onn/n=Onn

#include <bits/stdc++.h>using namespace std;const int maxn = 5000 + 5;int vis[maxn];struct Point{    long long x, y;    int idx;    Point operator + (const Point &other)const{return {x+other.x, y+other.y};}    Point operator - (const Point &other)const{return {x-other.x, y-other.y};}    Point operator * (const Point &other)const{return {x*other.x, y*other.y};}    bool operator < (const Point &other)const    {        if(x != other. x)   return x < other.x;        return y < other.y;    }};double Cross(Point A, Point B){return A.x*B.y - A.y*B.x;}//返回向量A和向量B的叉积int ConvexHull(Point p[], int n, Point sta[]){//如果在凸包的边可以有输入点,则两个while里用<=0,否则使用<0。    sort(p, p + n);    int m = 0;    while(m < n)    {        for(int i = 0; i < n; i++)        {            if(vis[p[i].idx]) continue;            while(m > 1 && Cross(sta[m-1] - sta[m-2], p[i] - sta[m-2]) < 0)            {                vis[sta[m-1].idx] = 0;                m--;            }            sta[m++] = p[i];            vis[sta[m-1].idx] = 1;        }        int k = m;        for(int i = n - 2; i >= 0; i --)        {            if(vis[p[i].idx]) continue;            while(m > k && Cross(sta[m-1] - sta[m-2], p[i] - sta[m-2]) < 0)            {                vis[sta[m-1].idx] = 0;                m--;            }            sta[m++] = p[i];            vis[sta[m-1].idx] = 1;        }    }    if(n > 1)   m--;    return m;}Point nodes[maxn], sta[maxn];int main(){    int n;    scanf("%d", &n);    for(int i = 0; i < n; i++)    {        int x, y;        scanf("%d%d", &x, &y);        nodes[i] = {x, y, i + 1};    }    ConvexHull(nodes, n, sta);    printf("%d\n", n);    for(int i = 0; i < n; i++)  printf("%d%c", sta[i].idx, " \n"[i + 1 == n]);    return 0;}

I - Cubes

题意:给你一个数字n,问它最少可以由几个3的幂加和得到。输出数量及方案。(n44777444

思路:大力感知了一下,感觉最多只有十几个所以暴力搜一搜,玄学剪枝最为致命,剪枝很关键。其他就。随便爆搜吧。

#include <bits/stdc++.h>using namespace std;int a[200], ans[200], mi[400];int ans_num = 0x3f3f3f3f;void dfs(int n, int up, int num, int a[]){    if(num > 15)    return;    if(n == 0)    {        if(num < ans_num)        {            ans_num = num;            for(int i = 0; i < num; i++)    ans[i] = a[i];        }        return ;    }    if(num + 1 >= ans_num)  return;    if(num + n / mi[up] >= ans_num)   return;    for(int i = up; i >= 1; i--)    {        if(mi[i] > n)   continue;        a[num] = i;        dfs(n - mi[i], i, num+1, a);    }}int main(){    int n;    scanf("%d", &n);    mi[0] = 1;    for(int i = 1; i <= 356; i++)   mi[i] = i * i * i;    dfs(n, 356, 0, a);    sort(a, a + ans_num);    printf("%d\n", ans_num);    for(int i = 0; i < ans_num; i++)    {        printf("%d%c", ans[i], " \n"[i + 1 == ans_num]);    }    return 0;}

K - Cutting (哈希+自然溢出)

题意:对于串 S1 和 S2,求一种将串 S1 任意分割为 3 个子串 A, B, C ,满足 A+B+C=S1,同时通过改变排序能构成 S2 串。串长5000.

思路:
  枚举三段[0,i-1],[i,j-1],[j,len-1]。然后我们通过哈希检验即可。一开始采用的是longlong 和 mod。但是我们发现,会T。
  可是如果我们改变思路,采用自然溢出,使用unsigned,采用自然溢出,不去取模。效率有极大的提升。
  但是我们还是会T。是因为base取了26。取质数比较好。取29即可。
然后我们发现会WA,大概是还是有冲突?在哈希符合条件的时候,遍历一遍串,看看是否真的符合。

#include <bits/stdc++.h>using namespace std;const int maxn = 5000 + 5;const int base = 29;typedef unsigned long long LL;char str1[maxn], str2[maxn];unsigned int mi[maxn];unsigned int hashStr1[maxn];unsigned int hashOfStr2;unsigned int calc(unsigned int a, unsigned int b, unsigned int c, int len2, int len3){    return ((a * mi[len2] + b) * mi[len3] + c);}bool judge(int al, int ar, int bl, int br, int cl, int cr){    int st = 0;    for(int i = 0; i < ar - al + 1; i++)    {        if(str2[st + i] != str1[al + i]) return false;    }    st += ar - al + 1;    for(int i = 0; i < br - bl + 1; i++)    {        if(str2[st + i] != str1[bl + i]) return false;    }    st += br - bl + 1;    for(int i = 0; i < cr - cl + 1; i++)    {        if(str2[st + i] != str1[cl + i]) return false;    }    return true;}void output(int i, int j){    puts("YES");    char ch = str1[i];    str1[i] = '\0';    puts(str1);    str1[i] = ch;    ch = str1[j];    str1[j] = '\0';    puts(str1+i);    str1[j] = ch;    puts(str1+j);}void solve(){    int len = strlen(str1);    unsigned int a = str1[0] - 'a';    for(int i = 1; i < len; i++)    {        unsigned int b = str1[i] - 'a';        for(int j = i + 1; j < len; j++)        {            unsigned int c = hashStr1[j];            if(calc(a, b, c, j-i, len-j) == hashOfStr2 && judge(0, i-1, i, j-1, j, len-1))   {output(i, j);return ;}            if(calc(a, c, b, len-j, j-i) == hashOfStr2 && judge(0, i-1, j, len-1, i, j-1))    {output(i, j);return ;}            if(calc(b, a, c, i, len-j) == hashOfStr2 && judge(i, j-1, 0, i-1, j, len-1))    {output(i, j);return ;}            if(calc(b, c, a, len-j, i) == hashOfStr2 && judge(i, j-1, j, len-1, 0, i-1))    {output(i, j);return ;}            if(calc(c, a, b, i, j-i) == hashOfStr2 && judge(j, len-1, 0, i-1, i, j-1))    {output(i, j);return ;}            if(calc(c, b, a, j-i, i) == hashOfStr2 && judge(j, len-1, i, j-1, 0, i-1))    {output(i, j);return ;}            b = b * base + str1[j] - 'a';        }        a = a * base + str1[i] - 'a';    }    puts("NO");    return ;}int main(){    mi[0] = 1;    for(int i = 1; i < maxn; i++)   mi[i] = mi[i - 1] * base;    scanf("%s", &str1);    scanf("%s", &str2);    int len = strlen(str2);    hashOfStr2 = 0;    for(int i = 0; i < len; i++)    {        hashOfStr2 = (hashOfStr2 * base + str2[i] - 'a');    }    len = strlen(str1);    hashStr1[len] = 0;    for(int i = len - 1; i >= 0; i--)    {        hashStr1[i] = (str1[i] - 'a') * mi[len - 1 - i] + hashStr1[i + 1];    }    solve();    return 0;}
阅读全文
0 0