cugb.20140417 (ural 1961 - 1970)

http://acm.hust.edu.cn/vjudge/contest/view.action?cid=44276#overview

http://acm.timus.ru/problemset.aspx?space=1&page=10  uarl 1961-1970


A

problem 

think

C(M, m) * C(N-M, n-m) / C(N, n) >= C(M, m-1) * C(N-M, n-m+1) / C(N, n)

C(M, m) * C(N-M, n-m) / C(N, n) >= C(M, m+1) * C(N-M, n-m-1) / C(N, n)

code

cin >> n >> m >> N;
M = min(N, (m * N + m) / n);
cout<<M<<endl;


B...In Chinese Restaurant

problem

给你n和m,n个人,前m个人要和第ki个人挨着(i!=ki),做成一个圈,1的位置固定,求方案数 n <= 100

think

给的m个人的需求,建立双向边,每个人的度最多是2,否则答案是0.

分成几个group,如果有圈,那么如果有1个group答案就是2,否则就是0。但是要特判n==2 , 答案就是1

如果每个group都是链,就是乘起来(group里面有1个点就是乘以1否则乘以2),再乘以group的组合数,由于1固定所以乘以(group-1)!

code

const LL mod = 1000000007;
const int N = 111;
int lft[N], rit[N];
int vis[N];
LL A[N];

bool quan;

int dfs(int s, int pre, int now){
    if(vis[s] != 0){
        quan = true;
        return 2;
    }
    vis[s] = now;
    if(lft[s] == -1 && rit[s] == -1) return 1;
    if(lft[s] != -1 && lft[s] != pre) dfs(lft[s], s, now);
    if(rit[s] != -1 && rit[s] != pre) dfs(rit[s], s, now);
    return 2;
}

int main(){
    int n, m;
    A[0] = 1LL;
    for(int i = 1; i <= 100; ++i) A[i] = A[i-1]*i % mod;
    while(scanf("%d%d", &n, &m) != EOF){
        memset(lft, -1, sizeof(lft));
        memset(rit, -1, sizeof(rit));
        bool nop = false;
        for(int i = 1; i <= m; ++i){
            int a;
            scanf("%d", &a);
            if(lft[i] != a && rit[i] != a){
                if(lft[i] == -1) lft[i] = a;
                else if(rit[i] == -1) rit[i] = a;
                else nop = true;
            }

            if(lft[a] != i && rit[a] != i){
                if(lft[a] == -1) lft[a] = i;
                else if(rit[a] == -1) rit[a] = i;
                else nop = true;
            }
        }
        if(nop){
            puts("0");
            continue;
        }
        if(n==2){
            puts("1");
            continue;
        }
        memset(vis, 0, sizeof(vis));
        int now = 0;
        LL ans = 1LL;
        quan = false;
        for(int i = 1; i <= n; ++i){
            if(vis[i] == 0){
                ans *= (LL)dfs(i, 0, ++now);
                ans %= mod;
            }
        }
        ans *= A[now-1];
        ans %= mod;
        if(quan && now > 1) puts("0");
        else printf("%I64d\n", ans);
    }
	return 0;
}

E.Pear Tree

problem

给你一个由1-n组成的一种排列方式,n是1e5,问你能不能拆成两个序列,使得两个序列都是单调的。能的话就输出任意一种方案。

think

瞪着别人的没有注释代码看了好几天。原来一遍又一遍重复又重复之后突然之间,会开窍。

code

/**
第二维:0表示上升序列,1表示下降序列
第0位表示a[i]在的那个序列,第1位表示另一个序列
如:01表示a[i]在的序列下降另一个序列上升

is[i][j]表示a[i]放在序列的id,0|1
dp[i][j]表示另一个(a[i]不在的那个序列)序列的目前最后一个数
[i,j]状态是由[i-1, pre[i][j]]状态转移过来的,
is和pre是为了最后输出结果
**/
const int N = 111111;
int dp[N][4];
int is[N][4];
int pre[N][4];
int a[N];

vector<int>ans[2];

int main(){
    int n;
    while(scanf("%d", &n) != EOF){
        memset(dp, -1, sizeof(dp));
        dp[0][0] = dp[0][1] = 0;//第二个序列是上升的
        dp[0][2] = dp[0][3] = N;
        ans[0].clear();
        ans[1].clear();
        for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
        for(int i = 0; i < n - 1; ++i){
            for(int j = 0; j < 4; ++j){
                if(dp[i][j] == -1) continue;
                bool fi = j & 1;
                bool se = j >> 1;
                if((a[i+1] > a[i]) ^ fi){//a[i+1]放在和a[i]相同的序列
                    if(dp[i+1][j] == -1 || ((dp[i+1][j] > dp[i][j])^se)){//放了会使状态变得更优
                        dp[i+1][j] = dp[i][j];
                        is[i+1][j] = is[i][j];
                        pre[i+1][j] = j;
                    }
                }
                if((a[i+1] > dp[i][j]) ^ se){
                    int jj = ((int)fi << 1) | se;
                    if(dp[i+1][jj] == -1 || ((dp[i+1][jj] > a[i]) ^ fi)){
                        dp[i+1][jj] = a[i];
                        is[i+1][jj] = is[i][j] ^ 1;
                        pre[i+1][jj] = j;
                    }
                }
            }
        }

        for(int j = 0; j < 4; ++j) if(dp[n-1][j] != -1){
            int jj = j;
            for(int i = n-1; i >= 0; --i){
                ans[is[i][jj]].push_back(a[i]);
                jj = pre[i][jj];
            }
            break;
        }
        if(ans[0].size() == 0){
            if(ans[1].size() == 0) puts("Fail");
            else{
                printf("%d %d\n%d\n%d", 1, n-1, ans[1][n-1], ans[1][n-2]);
                for(int i = n-3; i >= 0; --i) printf(" %d", ans[1][i]);
                puts("");
            }
        }
        else if(ans[1].size() == 0){
            printf("%d %d\n%d\n%d", 1, n-1, ans[0][n-1], ans[0][n-2]);
            for(int i = n-3; i >= 0; --i) printf(" %d", ans[0][i]);
            puts("");
        }
        else{
            printf("%d %d\n", ans[0].size(), ans[1].size());
            printf("%d", ans[0][ans[0].size()-1]);
            for(int i = ans[0].size() - 2; i >= 0; --i) printf(" %d", ans[0][i]);
            puts("");
            printf("%d", ans[1][ans[1].size()-1]);
            for(int i = ans[1].size() - 2; i >= 0; --i) printf(" %d", ans[1][i]);
            puts("");
        }
    }
	return 0;
}


J...皇后像廣場

problem

think

暴力回溯dfs竟然过了。。

code

int a[9][4][4][4];//9个方块,4个方向,4*4的元素
bool lft[9][4][9][4];
bool down[9][4][9][4];
int vis[10];
int zhu1[10];//用于存哪个方块
int zhu2[10];//用于存哪个方向

bool can(int n, int i, int ii){
    if(vis[i]) return false;
    if(n >= 3 && !down[zhu1[n-3]][zhu2[n-3]][i][ii]) return false;
    if(n%3 > 0 && !lft[zhu1[n-1]][zhu2[n-1]][i][ii]){
        return false;
    }
    return true;
}

bool dfs(int n){
    if(n == 9){
        for(int i = 0; i < 10; ++i){
            for(int j = 0; j < 10; ++j){
                if(i <= 2){
                    if(j <= 2) printf("%d ", a[zhu1[0]][zhu2[0]][i][j]);
                    else if(j <= 5) printf("%d ", a[zhu1[1]][zhu2[1]][i][j-3]);
                    else if(j <= 8) printf("%d ", a[zhu1[2]][zhu2[2]][i][j-6]);
                    else printf("%d\n", a[zhu1[2]][zhu2[2]][i][j-6]);
                }
                else if(i <= 5){
                    if(j <= 2) printf("%d ", a[zhu1[3]][zhu2[3]][i-3][j]);
                    else if(j <= 5) printf("%d ", a[zhu1[4]][zhu2[4]][i-3][j-3]);
                    else if(j <= 8) printf("%d ", a[zhu1[5]][zhu2[5]][i-3][j-6]);
                    else printf("%d\n", a[zhu1[5]][zhu2[5]][i-3][j-6]);
                }
                else if(i <= 9){
                    if(j <= 2) printf("%d ", a[zhu1[6]][zhu2[6]][i-6][j]);
                    else if(j <= 5) printf("%d ", a[zhu1[7]][zhu2[7]][i-6][j-3]);
                    else if(j <= 8) printf("%d ", a[zhu1[8]][zhu2[8]][i-6][j-6]);
                    else printf("%d\n", a[zhu1[8]][zhu2[8]][i-6][j-6]);
                }
            }
        }
        return true;
    }
    for(int i = 0; i < 9; ++i) for(int ii = 0; ii < 4; ++ii){
        if(can(n, i, ii)){
            vis[i] = 1;
            zhu1[n] = i;
            zhu2[n] = ii;
            if(dfs(n+1)) return true;
            vis[i] = 0;
        }
    }
    return false;
}

int main(){
    for(int i = 0; i < 9; ++i){
        for(int j = 0; j < 4; ++j){
            for(int k = 0; k < 4; ++k){
                scanf("%d", &a[i][0][j][k]);
            }
        }
        for(int h = 1; h <= 3; ++h){
            for(int j = 0; j < 4; ++j){
                for(int k = 0; k < 4; ++k){
                    a[i][h][3-k][j] = a[i][h-1][j][k];
                }
            }
        }
    }
    for(int i = 0; i < 9; ++i) for(int ii = 0; ii < 4; ++ii) {
        for(int j = 0; j < 9; ++j) for(int jj = 0; jj < 4; ++jj) {
            if(i != j){
                bool ok1 = 1;
                bool ok2 = 1;
                for(int k = 0; k < 4; ++k){
                    ok1 &= (a[i][ii][3][k] == a[j][jj][0][k]);
                    ok2 &= (a[i][ii][k][3] == a[j][jj][k][0]);
                }
                down[i][ii][j][jj] = ok1;
                lft[i][ii][j][jj] = ok2;
            }
        }
    }
    memset(vis, 0, sizeof(vis));
    dfs(0);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值