zoukankan      html  css  js  c++  java
  • 2012长春赛区题解(部分)

    总结起来自己还是太逗比,DP还是太弱,而DP却恰是算法思维能力的体现,现在要开始注重加强这方面的训练,遇到这类题目总是不敢想,令人担忧。

    Problem B ZOJ 3656 Bit Magic

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3656

    分析:

    总共N个数,每个b[i][j]会对a[i]和a[j]的对应二进制位产生一定影响,具体见题目,我们需要做的是判断每个数的每个位是0或1,根据关系建立边,然后直接2sat就可以了。

    做的时候一直MLE,发现怎么改都不可能过的。原来是我把每个数所有位都放在图里判断了,其实完全可以每次判断一个位,因为位与位之间毫无影响啊。

    代码:

    #include <bits/stdc++.h>
    #define esp 1e-8
    #define in freopen("solve_in.txt", "r", stdin);
    #define out freopen("out.txt", "w", stdout);
    #define pf(x) ((x)*(x))
    #define lowbit(x) ((x)&(-(x)))
    #define bug(x) printf("Line %d: >>>>>>
    ", (x));
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1|1
    #define pb push_back
    #define mp make_pair
    #define pi acos(-1.0)
    #define pf(x) ((x)*(x))
    
    using namespace std;
    typedef long long LL;
    typedef unsigned short US;
    
    const int maxn = 1100;
    stack<US> S;
    vector<US> g[maxn*2];
    bitset<maxn*2> mark;
    struct TwoSAT
    {
        int n, c;
    
        bool dfs(int x)
        {
            if(mark[x^1]) return false;
            if(mark[x]) return true;
            mark[x] = 1;
            S.push(x);
            for(int i = 0; i < (int)g[x].size(); i++)
            {
                if(!dfs(g[x][i])) return false;
            }
            return true;
        }
        void init(int n)
        {
            this->n = n;
            while(!S.empty()) S.pop();
            for(int i = 0; i  < 2*maxn; i++)
                g[i].clear();
            mark.reset();
        }
        void add(US x, US y)
        {
            g[x].pb(y);
        }
        bool solve(int n)
        {
            for(int i = 0; i < 2*n; i += 2)
            {
                if(!mark[i] && !mark[i+1])
                {
                    c = 0;
                    if(!dfs(i))
                    {
                        while(!S.empty()) mark[S.top()] = false, S.pop();
                        if(!dfs(i+1)) return false;
                    }
                }
            }
            return true;
        }
    } sat;
    int idx(int u, int v)
    {
        return u;
    }
    const int maxm = 555;
    
    int a[maxm][maxm];
    
    int main()
    {
    
        int n;
        while(scanf("%d", &n) == 1)
        {
    
    
            int ok = 1;
            for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) scanf("%d", &a[i][j]);
            for(int k = 0; k < 31 && ok; k++)
            {
                sat.init(2*n);
                for(int i = 0; i < n && ok; i++) for(int j = 0; j < n &&ok; j++)
                    {
                        int &y = a[i][j];
                        if(i == j)
                        {
                            if(y) ok = 0;
                            continue;
                        }
                        if(i%2 == 1 && j%2 == 1)
                        {
                            int u = idx(i, k);
                            int v = idx(j, k);
                            if(y&1)
                            {
                                sat.add(u<<1, v<<1|1);
                                sat.add(v<<1, u<<1|1);
                            }
                            else
                            {
                                sat.add(u<<1|1, u<<1);
                                sat.add(v<<1|1, v<<1);
    //                            sat.add(u<<1, v<<1);
    //                            sat.add(v<<1, u<<1);
                            }
                            y >>= 1;
                        }
                        else if(i%2 == 0 && j%2 == 0)
                        {
                            int u = idx(i, k);
                            int v = idx(j, k);
                            if(y&1)
                            {
                                sat.add(u<<1, u<<1|1);
                                sat.add(v<<1, v<<1|1);
                            }
                            else
                            {
                                sat.add(u<<1|1, v<<1);
                                sat.add(v<<1|1, u<<1);
                            }
                            y >>= 1;
                        }
                        else
                        {
                            int u = idx(i, k);
                            int v = idx(j, k);
                            if(y&1)
                            {
                                sat.add(u<<1, v<<1|1);
                                sat.add(v<<1, u<<1|1);
                                sat.add(u<<1|1, v<<1);
                                sat.add(v<<1|1, u<<1);
                            }
                            else
                            {
                                sat.add(u<<1, v<<1);
                                sat.add(v<<1, u<<1);
                                sat.add(u<<1|1, v<<1|1);
                                sat.add(v<<1|1, u<<1|1);
                            }
                            y >>= 1;
                        }
                    }
                if(!sat.solve(n*2))
                    ok = 0;
            }
            if(ok)
                puts("YES");
            else puts("NO");
        }
        return 0;
    }
    View Code

    Problem C ZOJ 3657 The Little Girl who Picks Mushrooms 简单题

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3657

    分析:

    对于n <= 3,结果显然为1024,n == 4时,需要判断能否从中取出3个数和位1024整数倍,能够的话结果为1024,或者判断任意3个数作为剩下的数,然后计算结果。

    n == 5则只能看能否取出3个数为1024整数倍,能则根据剩下的值计算结果。

    代码:

     1 #include <bits/stdc++.h>
     2 #define esp 1e-8
     3 #pragma comment(linker, "/STACK:102400000,102400000")
     4 #define in freopen("F:\rootial\data\data.txt", "r", stdin);
     5 #define IN freopen("solve_in.txt", "r", stdin);
     6 #define out freopen("out.txt", "w", stdout);
     7 #define pf(x) ((x)*(x))
     8 #define lowbit(x) ((x)&(-(x)))
     9 #define bug(x) printf("Line %d: >>>>>>
    ", (x));
    10 #define lson l, m, rt<<1
    11 #define rson m+1, r, rt<<1|1
    12 #define pb push_back
    13 #define mp make_pair
    14 #define pi acos(-1.0)
    15 #define pf(x) ((x)*(x))
    16 
    17 using namespace std;
    18 typedef long long LL;
    19 int a[10];
    20 
    21 int main(){
    22 
    23     int n;
    24     while(scanf("%d", &n) == 1){
    25         int sum = 0;
    26         for(int i = 0; i < n; i++){
    27                 scanf("%d", a+i), sum += a[i];
    28         }
    29         if(n <= 3) cout<<1024<<endl;
    30         else{
    31             int ans = 0;
    32             if(n == 4){
    33                 for(int i = 0; i < 4; i++)
    34                 for(int j = i+1; j < 4; j++){
    35                     int tmp = a[i]+a[j];
    36                     if(tmp <= 1024)
    37                         ans = max(tmp, ans);
    38                     else ans = max(ans, tmp%1024 + 1024*(tmp%1024 == 0));
    39                 }
    40                 for(int i = 0; i < n; i++) for(int j = i+1; j < n; j++) for(int k = j+1; k < n; k++)
    41                 {
    42                      int tmp = a[i]+a[j]+a[k];
    43                      if(tmp %1024 == 0)
    44                         ans = 1024;
    45                 }
    46             }
    47             else {
    48                 for(int i = 0; i < n; i++) for(int j = i+1; j < n; j++) for(int k = j+1; k < n; k++)
    49                 {
    50                     int tmp = a[i]+a[j]+a[k];
    51                     if(tmp%1024 == 0){
    52                         int s = sum-tmp;
    53                         if(s <= 1024)
    54                             ans = max(ans, s);
    55                         else ans = max(ans, s%1024 + 1024*(s%1024 == 0));
    56                     }
    57                 }
    58             }
    59             cout<<ans<<endl;
    60 
    61         }
    62 
    63     }
    64     return 0;
    65 }
    View Code

    Problem E ZOJ 3659 Conquer a New Region

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3659

    分析:

    题意是一棵树,每条边有一个正权值,一个点到另一点的花费是经过的路径上边的最小权值,要求一个中心,其他所有点到这个点的总花费最大。

    做法是这样的,按边权值从大到小排序,构建最大生成树,并查集里记录结点个数,及此时这个并查集构成的子树里的总花费,按权值依次选择每条边,遇到两个点不在同一并查集时,由于此时中心肯定在其中一个并查集里,所以计算这两种情况下,连接起来后总花费。因为此时这条边是两个子树里最小的,计算中心在另一并查集里时totalCost = 此并查集结点数目*边cost + 另一并查集花费,取两种情况下最大值作为合并后并查集的花费,并更新结点数目。最后一个并查集的总花费即为结果。

    代码:

     1 #include <bits/stdc++.h>
     2 #define pb push_back
     3 #define mp make_pair
     4 #define in freopen("data.txt", "r", stdin);
     5 #define bug(x) printf("Line : %u >>>>>>
    ", (x));
     6 
     7 using namespace std;
     8 typedef long long LL;
     9 typedef unsigned long long ULL;
    10 const int maxn = 200000 + 10;
    11 
    12 struct Edge{
    13     int u, v, c;
    14     Edge(){}
    15     Edge(int u, int v, int c):u(u), v(v), c(c){}
    16     bool operator < (const Edge &o)const{
    17         return c > o.c;
    18     }
    19 }e[maxn];
    20 struct Node{
    21     int f, num;
    22     LL cost;
    23     Node(){}
    24     Node(int f, int num, LL cost):f(f), num(num), cost(cost){}
    25 } fa[maxn];
    26 LL ans = 0;
    27 int findset(int x){ return x == fa[x].f ? x : fa[x].f = findset(fa[x].f);}
    28 void makeTree(int n){
    29     for(int i = 1; i <= n; i++) fa[i] = Node(i, 1, 0);
    30     for(int i = 0; i < n-1; i++){
    31         int u = e[i].u;
    32         int v = e[i].v;
    33         int c = e[i].c;
    34         int fu = findset(u);
    35         int fv = findset(v);
    36         if(fu != fv){
    37             LL ans1 = (LL)fa[fu].num*c + fa[fv].cost;
    38             LL ans2 = (LL)fa[fv].num*c + fa[fu].cost;
    39             ans1 = max(ans1, ans2);
    40             fa[fu].f = fv;
    41             fa[fv].cost = ans1;
    42             fa[fv].num += fa[fu].num;
    43             ans = max(ans, ans1);
    44         }
    45     }
    46 }
    47 int main() {
    48     
    49     int n;
    50     while(scanf("%d", &n) == 1){
    51         ans = 0;
    52         for(int i = 0; i < n-1; i++) scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].c);
    53         sort(e, e+n-1);
    54         makeTree(n);
    55         printf("%lld
    ", ans);
    56     }
    57     return  0;
    58 }
    View Code

    Problem H ZOJ 3662 Math Magic

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3662

    分析:

    问选取k个正数,和为N,最小公倍数为M,有多少种方法?考虑数的顺序。

    对于M首先分解质因数Div[i],记录每个Div[i]数目cnt[i],然后考虑k个数中每个数a[k],包好的质因数Div[i]的个数,由于LCM为M,每个Div[i], 要保证k个数中至少有一个数 包含有cnt[i]个。这里用bitmask表示,s相应位为1,表示Div[i]已经有a[k]包含cnt[i]个了

    思路是Dp的,考虑前k个数,和为sum,Div的包含状态表示为s时选取方案数目。

    转移时:dp[i+1][j+sum][s|ss] += dp[i][j][s];

    首先要预处理一下每个a[k]所有可能情况,也就是根据可能包含的Div[i]的数目,算出和sum,状态s,dfs处理一下。

    代码:

     1 #include <bits/stdc++.h>
     2 #define pb push_back
     3 #define mp make_pair
     4 #define in freopen("data.txt", "r", stdin);
     5 #define bug(x) printf("Line : %u >>>>>>
    ", (x));
     6 
     7 using namespace std;
     8 typedef long long LL;
     9 typedef unsigned long long ULL;
    10 const int M = (int)1e9 + 7;
    11 const int maxn = 1111;
    12 LL dp[111][maxn][1<<4];
    13 int p[maxn], vis[maxn], prime;
    14 
    15 void getPrime(int n) {
    16     for(int i = 2; i < n; i++) {
    17         if(vis[i] == 0) {
    18             p[prime++] = i;
    19             for(int j = i*i; j < n; j+=i)
    20                 vis[j] = 1;
    21         }
    22     }
    23 }
    24 vector<int> Div;
    25 int cnt[maxn];
    26 
    27 void getDiv(int n) {
    28     Div.clear();
    29     for(int i = 0; i < prime; i++) {
    30         if(n%p[i] == 0) {
    31             Div.pb(p[i]);
    32             int sz = Div.size()-1;
    33             cnt[sz] = 0;
    34             while(n%p[i] == 0) {
    35                 cnt[sz]++;
    36                 n /= p[i];
    37             }
    38             if(n == 1) break;
    39         }
    40     }
    41     if(n != 1) {
    42         Div.pb(n);
    43         cnt[Div.size()-1] = 1;
    44     }
    45 }
    46 vector<pair<int, int> > State;
    47 void getState(int pos, int sum, int s) {
    48     if(pos == 0) {
    49         State.pb(make_pair(sum, s));
    50         return;
    51     }
    52     int tmp = 1;
    53     for(int i = 0; i <= cnt[pos-1]; i++, tmp *= Div[pos-1]) {
    54         getState(pos-1, sum*tmp, s|(i == cnt[pos-1] ? (1<<(pos-1)) : 0));
    55     }
    56 }
    57 int main() {
    58 
    59     int n, m, k;
    60     getPrime(maxn);
    61     while(scanf("%d%d%d", &n, &m, &k) == 3) {
    62         getDiv(m);
    63         memset(dp, 0, sizeof dp);
    64         int sz = Div.size();
    65         State.clear();
    66         getState(Div.size(), 1, 0);
    67         dp[0][0][0] = 1;
    68         for(int i = 0; i < k; i++)
    69             for(int j = 0; j < n; j++) for(int s = 0; s < (1<<sz); s++) {
    70                     for(int kk = 0; kk < State.size(); kk++) {
    71                         int sum = State[kk].first;
    72                         int ss = State[kk].second;
    73                         if(j+sum <= n)
    74                             dp[i+1][j+sum][ss|s] = (dp[i+1][j+sum][ss|s] + dp[i][j][s])%M;
    75                     }
    76                 }
    77         cout<<dp[k][n][(1<<sz)-1]<<endl;
    78     }
    79     return 0;
    80 }
    View Code

     UPD:继续上K题.....

    Problem K ZOJ 3665 Yukari's Birthday

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3665

    分析:给定n <=10^12,然后求k,r,满足k^1+k^2+....k^r = n 或 n-1。且先满足k*r最小,然后r最小。(k>=2)

    由于k >= 2,容易算出对于最大的n,r <= 40,这样就为二分k提供了条件。枚举r,二分k,为了防止k过大时,计算溢出使用double。

    二分时注意右边界R <=n^(1/r),我是用exp(1.0/r*log(n)) + 1当做右边界,这样才能过。

    代码:

    #include <bits/stdc++.h>
    #define esp 1e-6
    #define pb push_back
    #define mp make_pair
    #define in freopen("data.txt", "r", stdin);
    #define bug(x) printf("Line : %d >>>>>>
    ", (x));
    
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    int dblcmp(double x) {
        if(fabs(x) < esp) return 0;
        return x > 0 ? 1 : -1;
    }
    LL n, ansr, ansk;
    int check(LL m, int r, LL n) {
        LL res = 1;
        for(int i = 0; i < r; i++)
            res *= m;
        res--;
        res /= m-1;
        res *= m;
        return dblcmp(res-n);
    }
    void getAns(int r, LL n) {
    
        LL L = 2, R;
        R = (LL)(exp(1.0/r*log(1.0*n))) + 1;
        while(L <= R) {
            LL m = (L+R)>>1;
            int sign = check(m, r, n);
            if(!sign) {
                if((double)r*m < (double)ansr*ansk ||
                        (dblcmp((double)r*m-(double)ansr*ansk) == 0 && r < ansr))
                    ansr = r, ansk = m;
                break;
            } else if(sign > 0) {
                R = m-1;
            } else {
                L = m+1;
            }
        }
    }
    int main() {
    
        while(scanf("%lld", &n) == 1) {
            ansr = (int)1e8, ansk = (int)1e8;
            int maxr = (int)(log(n)/log(2)) + 1;
            for(int r = 1; r <= maxr; r++) {
                getAns(r, n);
                getAns(r, n-1);
            }
            cout << ansr << ' ' << ansk << endl;
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Java反射(2)访问字段(Field)
    《编程珠玑》中“位图排序”引发的一系列实验
    Java : 传值or传引用?
    const 指针
    三种数据库访问——Spring3.2 + Hibernate4.2
    三种数据库访问——Spring JDBC
    数据库连接池:Druid
    三种数据库访问——原生JDBC
    介绍4款json的java类库 及 其性能测试
    云存储(Swift+Keystone)部署策略
  • 原文地址:https://www.cnblogs.com/rootial/p/4005596.html
Copyright © 2011-2022 走看看