2016-2017 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2016)

A Artwork

题目链接
题意:给你一个n*m的图,在这张图上选择(x1,y1)到(x2,y2)的矩形涂黑,问每一步之后剩下联通的白色块有多少个。
思路:并查集每个联通块,从后往前类撕封条的方法,将每个从黑变白的块与四周并查集,记录答案。

    #include<bits/stdc++.h>
    using namespace std;

    const int maxn = 1e3 + 5;

    struct node {
        int x1, x2, y1, y2;
    }op[10005];

    int g[maxn][maxn];
    int fa[maxn*maxn];
    int ans[10005];

    int dir[2][2] = {
        -1,0,
        0,-1
    };

    int dir2[4][2] = {
        -1, 0,
        0, -1,
        1, 0,
        0, 1};
    int n, m, p;

    int ok(int x, int y) {
        return x > 0 && y > 0 && x <= n && y <= m;
    }

    int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }

    void mix(int x, int y) {
        int fx = find(x);
        int fy = find(y);
        fa[fx] = fy;
    }

    void init() {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                int id = (i - 1) * m + j;
                fa[id] = id;
            }
        }
    }

    int main() {
        scanf("%d%d%d", &n, &m, &p);
        init();
        for (int i = 1; i <= p; i++) {
            scanf("%d%d%d%d", &op[i].x1, &op[i].y1, &op[i].x2, &op[i].y2);
            if (op[i].x1 == op[i].x2) {
                for (int y = op[i].y1; y <= op[i].y2; y++) {
                    g[op[i].x1][y] ++;
                }
            }
            else {
                for (int x = op[i].x1; x <= op[i].x2; x++) {
                    g[x][op[i].y1] ++;
                }
            }
        }

        // cout << "yyy" << endl;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                for (int k = 0; k < 2; k++) {
                    int x = dir[k][0] + i;
                    int y = dir[k][1] + j;
                    if (ok(x, y) && !g[i][j] && !g[x][y]) {
                        mix(((x - 1)*m + y), (i - 1)*m + j);
                    }
                }
            }
        }
        set<int>st;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if(!g[i][j]){
                    int id = (i - 1) * m + j;
                    fa[id] = find(fa[id]);
                    st.insert(fa[id]);
                    // cout << i<<' '<<j << "  ***  " << fa[id] << endl;
                }
            }
        }
        ans[p] = st.size();

        for (int i = p; i >1; i--) {
            ans[i-1] = ans[i];
            if (op[i].x1 == op[i].x2) {
                for (int y = op[i].y1; y <= op[i].y2; y++) {
                    g[op[i].x1][y] --;

                    // cout <<"!!"<< op[i].x1 << ' ' << y << ' ' << g[op[i].x1][y] << endl;
                    if(!g[op[i].x1][y]){
                        int id = (op[i].x1 - 1) * m + y;
                        fa[id] = id;
                        ans[i-1]++;
                        for (int k = 0; k < 4;k++){
                            int tx=op[i].x1+dir2[k][0];
                            int ty = y + dir2[k][1];
                            if(ok(tx,ty)&&!g[tx][ty]){
                                if(find((tx - 1) * m + ty)!=find(id)){
                                    mix((tx - 1) * m + ty, id);
                                    ans[i-1]--;
                                }
                            }
                        }
                    }
                }
            }
            else {
                for (int x = op[i].x1; x <= op[i].x2; x++) {
                    g[x][op[i].y1] --;

                    // cout << "??" << x << ' ' << op[i].y1 << ' ' << g[x][op[i].y1] << endl;
                    if(!g[x][op[i].y1]){
                        int id = (x - 1) * m + op[i].y1;
                        fa[id] = id;
                        ans[i-1]++;
                        for (int k = 0; k < 4;k++){
                            int tx=x+dir2[k][0];
                            int ty = op[i].y1 + dir2[k][1];
                            // cout << tx << " " << ty <<" "<<g[tx][ty]<< endl;
                            if(ok(tx,ty)&&!g[tx][ty]){
                                if(find((tx - 1) * m + ty)!=find(id)){
                                    mix((tx - 1) * m + ty, id);
                                    ans[i-1]--;
                                }
                            }
                        }
                    }
                }
            }

            // cout << ans[i - 1] << endl;
        }

        for (int i = 1;i<=p;i++)
            printf("%d\n", ans[i]);

        // system("pause");
        return 0;
    }

    /*
    4 6 5
    2 2 2 6
    1 3 4 3
    2 5 3 5
    4 6 4 6
    1 6 4 6
    */

B Bless You Autocorrect!

题目链接
题意:给定若干个字符串,可以打出若干个字符+tab进行自动补全,给你一个字符串求如何打步数最小
思路:字典树建边+bfs,注意单向边和双向边不同

    #include <bits/stdc++.h>

    #define maxn 1000005
    using namespace std;

    int n,m;
    char s[maxn];

    int trie[maxn][30];
    int tot=0;
    int root;

    vector <int>G[maxn];
    int val[maxn];
    int step[maxn];
    int pre[maxn];

    void insert(){
        int len=strlen(s);
        root=0;
        for(int i=0;i<len;i++){
            int id=s[i]-'a';
            if(!trie[root][id])
                trie[root][id]=++tot;

            val[trie[root][id]]++;
            G[root].push_back(trie[root][id]);
            G[trie[root][id]].push_back(root);
            pre[trie[root][id]]=root;
            root=trie[root][id];
        }
        int lst=root;
        for(int i=lst;i!=0;i=pre[i]){
            if(i==lst)continue; 
            if(val[i]==1){
                G[i].push_back(lst);
            }else
                break;
        }
    }

    void find(){
        int len=strlen(s);
        root=0;
        int ans=0;
        for(int i=0;s[i];i++){
            int x=s[i]-'a';
            if(trie[root][x]==0)
                break;
            root=trie[root][x];
            ans++;
        }

        if(!root)
            printf("%d\n",len);
        else
            printf("%d\n",len-ans+step[root]);
    }

    void bfs(){
        queue <int>q;
        q.push(0);
        step[0]=0;
        while(!q.empty()){
            int f=q.front();
            q.pop();

            for(int i=0;i<G[f].size();i++){
                if(G[f][i]==f)continue;
                if(!step[G[f][i]]){
                    step[G[f][i]]=step[f]+1;
                    q.push(G[f][i]);
                }    
            } 
        }

    }

    void input(){
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++){
            scanf("%s",s);
            insert();
        }

        bfs();
    }

    void solve(){
        for(int i=0;i<m;i++){
            scanf("%s",s);
            find();
        }
    }

    int main(){
        input();
        solve();
    } 

    /*
    5 5
    austria
    autocorrect
    program
    programming
    computer

    autocorrelation
    */

C Card Hand Sorting

题目链接
题意:52张扑克,现在给你n张牌,要求最后排序的方式是同一类花色在一块并且单调递增,求最小需要更改几张顺序。
思路:52张扑克同一花色连续且递增的方案可以枚举写出,对于给定的序列,和枚举出的结果进行求最长公共子序列。n-得到最大答案就是答案。

    #include <bits/stdc++.h>

    using namespace std;

    char hs[4] = {'s', 'h', 'd', 'c'};
    int hs1[4] = {0,1, 2, 3};
    char val[13] = {'2', '3', '4', '5', '6', '7', '8', '9','T', 'J', 'Q', 'K', 'A'};

    char str[55][3];
    char cmpstr[4][13][5];
    int n, dp[102][102];


    void input(){

        scanf("%d", &n);
        for (int i = 1; i <= n;i++){
            scanf("%s", str[i]);
        }

        // for (int i = 1; i <= n;i++){
        //     printf("%s ", str[i]);
        // }

        int maxx = -1;
        do
        {
            int flag[4] = {0};
            for (int i = 0; i < 16; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    if ((i & (1 << j) )== 0)
                        flag[j] = 0;
                    else
                        flag[j] = 1;
                }

                int cnt = 0;

                // for (int j = 0; j < 4;j++)
                //     printf("%d:%d\n", j, flag[j]);

                for (int j = 0; j < 4;j++){
                    cnt = 0;
                    if(flag[j]==0){
                        for (int k = 0; k < 13;k++){
                            cmpstr[j][cnt][0] = val[k];
                            cmpstr[j][cnt][1] = hs[hs1[j]];
                            cmpstr[j][cnt][2] = '\0';
                            cnt++;
                        }
                    }else
                    {
                        for (int k = 12; k >=0;k--){
                            cmpstr[j][cnt][0] = val[k];
                            cmpstr[j][cnt][1] = hs[hs1[j]];
                            cmpstr[j][cnt][2] = '\0';
                            cnt++;
                        }
                    }
                }

                // for (int j = 0; j < 4;j++){
                //     for (int k = 0; k < cnt; k++)
                //         printf("%s", cmpstr[j][k]);
                // }
                // printf("\n");

                for (int j = 0; j < 4;j++){
                    for (int l = 0; l< cnt;l++){
                        for (int k = 1; k <= n;k++){
                            // cout<<cmpstr[j][l]<<" "<<str[k]<<endl;
                            if(strcmp(cmpstr[j][l],str[k])==0)
                                dp[j * cnt + l + 1][k - 1 + 1] = dp[j * cnt + l][k - 1] + 1;
                            else
                                dp[j * cnt + l + 1][k - 1 + 1] = max(dp[j * cnt + l+1][k - 1], dp[j * cnt + l][k - 1+1]);
                        }
                    }
                }

                maxx = max(maxx, dp[4*cnt][n]);
            }
        } while (next_permutation(hs1, hs1 + 4));

        printf("%d", n - maxx);
    }

    int main(){
        input();
        // solve();
    }

    /*
    4
    2h Th 8c Qh
    */

D Daydreaming Stockbroker

题目链接
题意:股票模拟,主人公有100元钱,给出n天里每天的股票价格,即x元1股,主人公同时持有股不得超过1e5,问主人公最终最多能持有多少钱。
思路:最低点买入最高点卖出


    #include<iostream>  
    #include<cstdio>
    #include<cstring>
    #include<map>
    #include<vector> 
    #include<set>
    #include<queue>
    #include<algorithm>
    #include<string>
    #include<cmath>
    #include<stack>
    #include<functional>
    #include<bitset>
    #define ll long long
    #define maxn 100002
    const int mod = 1e9 + 7;

    using namespace std;
    int main(void){
        ll num[402];
        ll t, i, has = 0, price, money = 100;
        scanf("%lld",&t);
        for(i = 0;i < t;i++){
            scanf("%lld",&num[i]);
        }
        for(i = 0;i < t - 1;i++){
            if(num[i] < num[i + 1]){
                if(has < 100000){
                    if(100000 * num[i] < money){
                        price = num[i];
                        has = 100000;
                        money -= num[i] * 100000;
                    }
                    else{
                        price = num[i];
                        has += money / num[i];
                        money %= num[i];
                    }
                }
            }
            else{
                money += has * num[i];
                has = 0;
            }
        }
        if(num[t - 1] >= price){
            money += has * num[t - 1];
        }
        else{
            money += has * t;
        }
        printf("%lld\n",money);
        return 0;
    }

E Exponial

题目链接
题意:欧拉降幂

F Fleecing the Raffle

题目链接
题意:现在一个抽奖箱里面有n张写有n个人名字的卡片,需要从中抽取p张作为得奖主。现在你想在抽奖箱中加入k张写有你的名字的卡片,你需要确定一个k,使得抽到你且仅抽到你一次的概率最大。并输出该最大概率。
思路:概率化简计算

    #include<iostream>  
    #include<cstdio>
    #include<cstring>
    #include<map>
    #include<vector> 
    #include<set>
    #include<queue>
    #include<algorithm>
    #include<string>
    #include<cmath>
    #include<stack>
    #include<functional>
    #include<bitset>
    #define ll long long
    #define maxn 100002
    const int mod = 1e9 + 7;

    using namespace std;
    int main(void){
        double pre = 0;
        int n, p, i, j;
        scanf("%d %d",&n,&p);
        for(i = 1;;i++){
            double temp = 1.0 * i * p / (n - p + 1);
            for(j = 0;j < p;j++){
                temp = temp * (n - j) / (n - j + i);
            }
            if(temp > pre){
                pre = temp;
            }
            else{
                break;
            }
        }
        printf("%.10f\n",pre);
        return 0;
    }

G Game Rank

题目链接
题意:炉石的晋级规则,给出胜利和失败的次数求最后等级
思路:模拟

    #include<iostream>  
    #include<cstdio>
    #include<cstring>
    #include<map>
    #include<vector> 
    #include<set>
    #include<queue>
    #include<algorithm>
    #include<string>
    #include<cmath>
    #include<stack>
    #include<functional>
    #include<bitset>
    #define ll long long
    #define maxn 100002
    const int mod = 1e9 + 7;

    using namespace std;
    char ch[10002];
    int rk, star, win;
    void add(int x){
        star += x;
        if(rk > 20){
            if(star > 2){
                star -= 2;
                rk--;
            }
        }
        else if(rk > 15){
            if(star > 3){
                star -= 3;
                rk--;
            }
        }
        else if(rk > 10){
            if(star > 4){
                star -= 4;
                rk--;
            }
        }
        else if(rk){
            if(star > 5){
                star -= 5;
                rk--;
            }
        }
    }
    void sub(int x){
        if(rk < 20&&rk||rk == 20&&star){
            star--;
            if(star < 0){
                rk++;
                if(rk <= 10){
                    star = 4;
                }
                else if(rk <= 15){
                    star = 3;
                }
                else{
                    star = 2;
                }
            }
        }
    }
    int main(void){
        int i, len;
        scanf("%s",ch);
        strlen(ch);
        rk = 25;
        star = win = 0;
        len = strlen(ch);
        for(i = 0;i < len;i++){
            if(ch[i] == 'W'){
                win++;
                if(win >= 3&&rk >= 6){
                    add(2);
                }
                else{
                    add(1);
                }
            }
            else{
                win = 0;
                sub(1);
            }
        }
        if(rk){
            printf("%d\n",rk);
        }
        else{
            puts("Legend");
        }
        return 0;
    }

J Jumbled Compass

题目链接
题意:指南针 给出原度数和现度数,求旋转角度差
思路:水

    #include <bits/stdc++.h>

    #define inf 0x3f3f3f3f3f3f3f3f
    #define maxn 1005
    typedef long long ll;
    using namespace std;

    int n1, n2;
    void input(){
        scanf("%d%d", &n1, &n2);
        int ans = n2 - n1;
        if(ans<0)
            ans+=360;

        if(ans>180)
            ans = ans - 360;
        printf("%d\n", ans);
    }

    int main(){

        input();
        // solve();

        // printf("\n");
        // system("pause");
        return 0;
    }