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: >>>>>>\n", (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;
}

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: >>>>>>\n", (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 }

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 >>>>>>\n", (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\n", ans);
56     }
57     return  0;
58 }

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 >>>>>>\n", (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 }

时间: 2024-10-08 16:24:30

2012长春赛区题解(部分)的相关文章

hdu 4277 2012长春赛区网络赛 dfs+hashmap ***

hashmap判重大法好 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<queue> 7 #include<map> 8 using namespace std; 9 #define MOD 1000000007 10 const int INF=0

hdu 4273 2012长春赛区网络赛 三维凸包中心到最近面距离 ***

新模板 1 /* 2 HDU 4273 Rescue 3 给一个三维凸包,求重心到表面的最短距离 4 模板题:三维凸包+多边形重心+点面距离 5 */ 6 7 #include<stdio.h> 8 #include<algorithm> 9 #include<string.h> 10 #include<math.h> 11 #include<stdlib.h> 12 using namespace std; 13 const int MAXN=

hdu 4272 2012长春赛区网络赛 dfs暴力 ***

总是T,以为要剪枝,后来发现加个map就行了 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<queue> 7 #include<map> 8 using namespace std; 9 #define MOD 1000000007 10 const

hdu 4274 2012长春赛区网络赛 树形dp ***

设定每个节点的上限和下限,之后向上更新,判断是否出现矛盾 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<queue> 7 #include<map> 8 using namespace std; 9 #define MOD 1000000007 10

HDU - 4813 Hard Code (长春赛区水题)

Description Some strange code is sent to Da Shan High School. It's said to be the prophet's note. The note is extremely hard to understand. However, Professor Meng is so smart that he successfully found the pattern of the code. That is, the length of

HDU - 4814 Golden Radio Base (长春赛区B题)

最小二乘法又叫做最小平方法,是一种数学优化技术.它通过最小化误差的平方和寻找数据的最佳函数匹配. 通常情况下最小二乘法用于求回归问题.以简单的线性最小二乘为例,二维平面上给定个点的坐标,确定一条直线, 要求大致符合这个点的走向. 我们可以设这条直线的方程为,那么就要使在处的函数值与给定的值相 差达到最小,也就是说,要确定的值,使得 最小.根据这种方法求的值就是典型的最小二乘法. 可以看出是的一个二元函数,要求的最小值,那么求偏导,有 进一步得到 然后联立两式可以解出,如果方程数比较多,我们可以用

hdu4271 Find Black Hand 2012长春网络赛E题 最短编辑距离

hdu4271 Find Black Hand  2012长春网络赛E题  最短编辑距离 Find Black Hand Time Limit : 5000/2000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submission(s) : 19   Accepted Submission(s) : 1 Problem Description I like playing game with my friend

【2012长春区域赛】部分题解 hdu4420—4430

这场比赛特点在于两个简单题太坑,严重影响了心情..导致最后只做出两题....当然也反映出心理素质的重要性 1002: 题意:一个矩阵b[n][n]通过数组 a[n]由以下规则构成,现在已知b[n][n]问是否有对应的数组a[n] 解法: 首先都是位运算所以不同位是不会互相影响的,即可按位考虑. 又发现,只要知道a[0]就可以算出通过b[0][]算出所有的a[],这样可以假设a[0]为0或1,由b[0][]得到一个完整的数组a[],再check这个数组a是否能正确的得到其他的b[][]即可 时间复

2015长春赛区总结(其实是流水账

10月16日 呐,这是人生第一次区域赛呀!好激动好激动! 去呢,是坐火车到重庆,然后重庆坐灰机到长春. 由于火车票是16号早上9点,而且那天有CF,就直接通宵了!通宵肝了CF,涨了60分,写完题解就坐着妈妈的车,滚去火车站了= = 感谢妈妈给窝准备的N瓶水和K瓶牛奶,在机场由于是液体,就全扔了…… 火车上睡啊睡,睡啊睡,飞机上睡啊睡,睡啊睡. 下长春,晚上9点,一阵冷风吹醒,冻成傻逼辣…… 和柱爷他们会合之后,就滚去酒店睡啊睡. 10月17日 第二天 睡到11点,然后滚去东北师范的食堂吃饭辣.