• 【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
  • 相关阅读:
    D-Power Products
    B2
    软考知识点梳理--螺旋模型
    软考知识点梳理--敏捷方法
    软考知识点梳理--瀑布模型
    软考知识点梳理--统一软件开发过程RUP
    软考知识点梳理--信息系统生命周期
    软考知识点梳理--信息资源管理
    软考知识点梳理--以太网
    软考知识点梳理--应急储备与管理储备
  • 原文地址:https://www.cnblogs.com/oneshot/p/4389511.html
Copyright © 2020-2023  润新知