【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[][]即可

时间复杂度约为32*2*n^2 对于n=1000是可以接受的

当然队友是用2-SAT做的 吊吊吊吊吊orz 我就没写了,这里贴上队友的代码

代码:

#include<cstdio>
#include<vector>
#include<cstring>
#include <iostream>
#include <stdio.h>
using namespace std;

const int maxn = 510;

struct TwoSAT
{
    int n;
    vector<int> G[maxn * 2];
    bool mark[maxn * 2];
    int S[maxn * 2], c;

    bool dfs(int x)
    {
        if(mark[x ^ 1]) return false;
        if(mark[x]) return true;
        mark[x] = true;
        S[c++] = x;
        for(int i = 0; i < G[x].size(); i++)
            if(!dfs(G[x][i])) return false;
        return true;
    }

    void init(int n) // 一定要注意初始化的点数,别弄错
    {
        this->n = n;
        for(int i = 0; i < n * 2; i++) G[i].clear();
        memset(mark, 0, sizeof(mark));
    }

    // x = xval or y = yval
    void add_clause(int x, int xval, int y, int yval) // 编号从0~n-1
    {
        x = x * 2 + xval;
        y = y * 2 + yval;
        G[x ^ 1].push_back(y);
        G[y ^ 1].push_back(x);
    }

    // 当x==xval 时可推导出 y==yval
    void add_edge(int x, int xval, int y, int yval)
    {
        x = x * 2 + xval;
        y = y * 2 + yval;
        G[x].push_back(y);
    }

    bool solve()
    {
        for(int i = 0; i < n * 2; i += 2)
            if(!mark[i] && !mark[i + 1])
            {
                c = 0;
                if(!dfs(i))
                {
                    while(c > 0) mark[S[--c]] = false;
                    if(!dfs(i + 1)) return false;
                }
            }
        return true;
    }
};

TwoSAT solver;
int n;
int a[maxn][maxn];

bool check(int l)
{
    solver.init(n);
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
        {
            int bit = (a[i][j] & (1 << l))>>l;
            
            if(i == j)
            {
                if(a[i][j] != 0)
                {
                    return false;
                }
            }
            else if(a[i][j] != a[j][i])
            {
                return false;
            }
            else if(i % 2 == 0 && j % 2 == 0) // &
            {
                solver.add_edge(i, 1, j, bit);
                solver.add_edge(j, 1, i, bit);
                if(bit)
                {
                    solver.add_edge(i, 0, i, 1);
                    solver.add_edge(j, 0, j, 1);
                }
            }
            else if(i % 2 == 1 && j % 2 == 1) // |
            {
                solver.add_edge(i, 0, j, bit);
                solver.add_edge(j, 0, i, bit);
                if(!bit)
                {
                    solver.add_edge(i, 1, i, 0);
                    solver.add_edge(j, 1, j, 0);
                }
            }
            else // ^
            {
                solver.add_edge(i, 1, j, bit ^ 1);
                solver.add_edge(j, 1, i, bit ^ 1);
                solver.add_edge(i, 0, j, bit);
                solver.add_edge(j, 0, i, bit);
            }
        }
    return solver.solve();
}


int main()
{
    

    while(~scanf("%d", &n))
    {
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++)
            {
                scanf("%d", &a[i][j]);
            }
        bool flag = true;
        // 枚举每一位,l为座椅的 位数
        for(int l = 0; l <= 31; l++)
        {
            if(!check(l))
            {
                flag = false;
                break;
            }
        }
        puts(flag?"YES":"NO");
    }

    return 0;
}
View Code


1003:

题意:

这个简单题题意挺恶心的。。先开始一直没读懂。。

小明要在五座山上采五堆蘑菇,每堆的个数是0~2012,采完后必须送出三堆和为1024倍数的蘑菇(否则全送出),回家之前如果总数大于1024还要一直被抢1024。

现在已经采了n堆(n<=5),剩下的可以任意采(0~2012)问最终最多能拿回家多少蘑菇.

解法:

分情况特判.....以下省略好多字

代码:

#include <set>
#include <map>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
int a[6];
const int mod = 20121024;
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
    #endif
    int n;
    while (~scanf ("%d", &n))
    {
        memset(a, 0, sizeof(a));
        int sum = 0;
        for (int i = 0; i < n; i++)
        {
            scanf ("%d", a+i);
            sum += a[i];
        }
        int ans = 0, res = 0;
        if (n<=3)
        {
            printf("%d
", 1024);
            continue;
        }
        if (n == 4)
        {
            int ans = 0;
            bool flag = 0;
            for (int i = 0; i < 4; i++)
            {
                for (int j = i+1; j < 4; j++)
                {
                    int tmp = a[i]+a[j];
                    if (tmp)
                    ans = max(ans, (tmp%1024) ? (tmp%1024) : 1024);
                    for (int k = j +1; k < 4; k++)
                    {
                        if ((a[i] + a[j] + a[k]) % 1024 == 0)
                            flag = 1;
                    }
                }
            }
            if (flag)
                printf("%d
" , 1024);
            else
                printf("%d
" ,ans);
            continue;
        }
        if (n == 5)
        {
            bool f = 0;
            int  ans = 0;
            for (int i = 0; i < 5; i++)
            {
                for (int j = i+1; j < 5; j++)
                {
                    for (int k = j+1; k < 5; k++)
                    {
                        int tmp = a[i] +a[j] +a[k];
                        if (tmp % 1024 == 0)
                        {
                            if (sum-tmp)
                            ans = max(ans, ((sum-tmp)%1024) ? (sum-tmp)%1024 : 1024);
                        }
                    }
                }
            }
            printf("%d
",(ans > 1024) ? (ans %1024) : ans);
        }
    }
    return 0;
}
View Code


1004:

题意:

 求y的取值范围

思路:

高中数学题,移项得到一个二次函数,然后各种分类讨论,太麻烦了没敢写。。。

1005:

题意:

一颗给定的无根树里面有边权,要求选定一个根使得此根到所有节点的代价最大,代价定义为路径上边权的最小值

解法:

奇怪的贪心,先按边权大到小排序,然后加边,并查集维护,每次合并的时候贪心的取代价最小(还是不知道为什么是对的)

代码:

 1 #include <set>
 2 #include <map>
 3 #include <cmath>
 4 #include <ctime>
 5 #include <queue>
 6 #include <stack>
 7 #include <cstdio>
 8 #include <string>
 9 #include <vector>
10 #include <cstdlib>
11 #include <cstring>
12 #include <iostream>
13 #include <algorithm>
14 using namespace std;
15 typedef unsigned long long ull;
16 typedef long long ll;
17 const int inf = 0x3f3f3f3f;
18 const double eps = 1e-8;
19 const int maxn = 2e5+10;
20 int pa[maxn],rnk[maxn];
21 struct Edge
22 {
23     int frm, to, cst;
24     bool operator < (const Edge &rhs)const
25     {
26         return cst > rhs.cst;
27     }
28 }e[maxn];
29 int find (int x)
30 {
31     return pa[x] = (pa[x] == x ? x : find(pa[x]));
32 }
33 ll ans[maxn];
34 void Merge(int x, int y, int cost)
35 {
36     int fx = find(x);
37     int fy = find(y);
38     if (rnk[fx] < rnk[fy])
39         swap(fx, fy);
40     ans[fx] = max(ans[fy]+(ll)rnk[fx]*cost, ans[fx]+(ll)rnk[fy]*cost);
41     pa[fy] = fx;
42     rnk[fx] += rnk[fy];
43 }
44 void init ()
45 {
46     memset(ans, 0, sizeof(ans));
47     for (int i = 0; i < maxn; i++)
48     {
49         pa[i] = i;
50         rnk[i] = 1;
51     }
52 }
53 int main()
54 {
55     #ifndef ONLINE_JUDGE
56         freopen("in.txt","r",stdin);
57     #endif
58     int n;
59     while (~scanf ("%d", &n))
60     {
61         int u, v, c;
62         init();
63         for (int i = 0; i < n-1; i++)
64         {
65             scanf ("%d%d%d", &u, &v, &c);
66             e[i].frm = u, e[i].to = v, e[i].cst = c;
67         }
68         sort (e,e+n-1);
69         for (int i = 0; i < n-1; i++)
70         {
71             Merge(e[i].frm, e[i].to, e[i].cst);
72         }
73         printf("%I64d
", ans[find(1)]);
74     }
75     return 0;
76 }
View Code

1008:

题意:

知道n个数的和sum,以及n个数的LCM,求合法的组成方案(排列)

解法:
发现lcm的转移只可能通过lcm的约数,(一开始和分解质因数搞呢,后来经过学长提醒发现直接找出约数即可 orz),约数数量不是很多。。这样就可以dp了

把约数哈希一下 dp[i][j][k]代表考虑到第i个数,当前lcm为总LCM的第j个约数,当前sum为k的方案数,转移很容易

代码:

#include <iostream>
#include <stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<ctype.h>
using namespace std;
#define MAXN 10000
const int mod=1e9+7;
bool is(int p)
{
    for(int i=2; i*i<=p; i++)
    {
        if(p%i==0)
            return 0;
    }
    return 1;
}
int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b)
{
    return a/gcd(a,b)*b;
}
int prime[1010];
int s[50];
int dp[110][1100][50];
int a[1010];
int ha[1010];
int l[50][50];
int sum,L,n,m;
int main()
{
   // freopen("in.txt","r",stdin);
    m=0;
    for(int i=2; i<=1000; i++)
    {
        if(is(i))
        {
            prime[m++]=i;
        }
    }
    while(scanf("%d%d%d",&sum,&L,&n)!=EOF)
    {
        memset(ha,-1,sizeof(ha));
        int lim=0;
        for(int i=1;i<=L;i++)
        {
            if(L%i==0)
            {
                ha[L/i]=lim;
                s[lim++]=L/i;
            }
        }
        for(int i=0;i<lim;i++)
        {
            for(int j=0;j<lim;j++)
            {
                l[i][j]=lcm(s[i],s[j]);
            }
        }
        memset(dp,0,sizeof(dp));
        dp[0][0][lim-1]=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=sum;j++)
            {
                for(int k=0;k<lim;k++)
                {
                    if(!dp[i-1][j][k])
                        continue;
                    for(int t=0;t<lim;t++)
                    {
                        if(j+s[t]<=sum)
                        {
                            if(ha[l[k][t]]==-1)
                                continue;
                            dp[i][j+s[t]][ha[l[k][t]]]+=dp[i-1][j][k];
                            dp[i][j+s[t]][ha[l[k][t]]]%=mod;
                        }
                    }
                }
            }
        }
        cout<<dp[n][sum][0]<<endl;
    }
    return 0;
}
View Code

1010:

题意:
一个大矩形被一些线段分成了小矩形,现在给定两个点的坐标,求出删除一些线段使这两点在同一矩形后剩余矩形数量的最大值。

思路:

其实就是要找所求两点共同所在的最小的矩形(除此之外的线段都不删除,得到的剩余矩形数肯定最多)。

而按照题意的分割矩形法其实就是形成了一颗树,这样就发现两个点所在的最小矩形其实是这两个点当前所在矩形的lca

理论ac了。。代码还没写

1011:

题意:

给定一个等比数列 1(或者0)+k+k^2+....k^r的和 S,要求求出r 和k,多解首先满足r*k最小,然后满足 r最小

解法:

由于k>=2所以可以计算发现r最大为40,则可以枚举r,二分求得k ,如果r和二分出的k刚好等于 k或者k-1 则符合题意,可以统计答案

坑点是二分过程中容易溢出

代码:

#include <iostream>
#include <stdio.h>
using namespace std;
typedef long long ll;

ll n;

ll equ(ll ak , int r)
{
    ll ans = 1;
    ll k = ak;
    for(int i = 1; i <= r; i++)
    {
        ans += k;
        if(ans > n + 1)
            break;
        if(ans<=0)
            return 10000000000000LL;
        k *= ak;
    }
    return ans;
}

int main()
{
    
    while(~scanf("%I64d", &n))
    {
        ll ansk = 10000000000000LL;
        int ansr = 100;

        // 枚举r
        for(int r = 1; r <= 40; r++)
        {
            // 二分k
            ll l = 1, R = 1000000000001LL;
            while(l < R)
            {
                ll m = (l + R + 1) / 2;
                if(equ(m, r) <= n)
                {
                    l = m;
                }
                else
                {
                    R = m - 1;
                }
            }

            ll k = l;
            if(equ(k, r) == n)
            {
                // 保存答案
                if((ansk * ansr > k * r) || ((ansk * ansr == k * r) && ansr > r))
                {
                    ansk = k;
                    ansr = r;
                }
            }

            // 二分k
            l = 1, R = 1000000000001LL;
            while(l < R)
            {
                ll m = (l + R + 1) / 2;
                if(equ(m, r) <= n + 1)
                {
                    l = m;
                }
                else
                {
                    R = m - 1;
                }
            }

            k = l;
            if(equ(l, r) == n + 1)
            {
                // 保存答案
                if((ansk * ansr > k * r) || ((ansk * ansr == k * r) && ansr > r))
                {
                    ansk = k;
                    ansr = r;
                }
            }
        }
        printf("%d %I64d
", ansr, ansk);
    }


    return 0;
}
View Code
原文地址:https://www.cnblogs.com/oneshot/p/4389511.html