zoukankan      html  css  js  c++  java
  • 2016 CCPC 网络赛 B 高斯消元 C 树形dp(待补) G 状压dp+容斥(待补) H 计算几何

    2016 CCPC 网络赛

    A - A water problem 水题,但读题有个坑,输入数字长度很大。。

    B - Zhu and 772002

    题意:给出n个数(给出的每个数的质因子最大不超过2000),选出多个数相乘得b。问有多少种选法让b 为完全平方数。

    tags:高斯消元,求异或方程组解的个数。   好题

    每个数先素数分解开。  对于2000以内的每个素数p[i],这n个数有奇数个p[i]则系数为1,偶数个则系数为0,最后n个数的p[i]系数异或和都要为0才会使得最后的积为完全平方数。    所以,构建出系数矩阵,然后高斯消元,解出异或方程组的秩rank,自由变元的数量即为 x=n-rank,最后解的个数为2^x,但要注意减去全部为0的一种情况。

    #include<bits/stdc++.h>
    using namespace std;
    #pragma comment(linker, "/STACK:102400000,102400000")
    #define rep(i,a,b) for (int i=a;i<=b;i++)
    #define per(i,b,a) for (int i=b;i>=a;i--)
    #define mes(a,b)  memset(a,b,sizeof(a))
    #define INF 0x3f3f3f3f
    typedef long long ll;
    const int N = 2000, M=310, mod=1000000007;
    
    int pri[N], cnt;   bool mark[N+1];
    void sieve_prime()
    {
        rep(i,2,N) {
            if(mark[i]==0) pri[++cnt]=i;
            for(int j=i*i; j<=N; j+=i) mark[j]=1;
        }
    }
    
    int fpow(ll a,int b){ll ans=1; for(;b;b>>=1,a=a*a%mod) if(b&1) ans=ans*a%mod; return (int)ans;}  
    int n, mat[M][M];
    ll a[N];
    int gauss_rank(int c[][M])    //高斯消元求异或方程组的秩 
    {
        int i=1, j=1;
        while(i<=cnt && j<=n) {
            int r=0;
            rep(k,i,cnt) if(mat[k][j]) { r=k; break; }
            if(r) 
            {  
                swap(mat[r], mat[i]);
                rep(k,i+1,cnt) if(mat[k][j]) {
                    rep(h,i,n) mat[k][h]^=mat[i][h];
                }
                i++;
            }
            j++;
        }
        return i-1;
    }
    int solve()
    {
        rep(j,1,n) {
            ll tmp=a[j];       
            rep(i,1,cnt) {    //构建系数矩阵 
                while(tmp%pri[i]==0) { 
                    tmp/=pri[i], mat[i][j]^=1;
                }
            }
        }
        int x=n-gauss_rank(mat);     //自由变元数量 
        return (fpow(2,x)-1+mod)%mod;
    }
    int main()
    {
        sieve_prime();
        int T;   scanf("%d", &T);
        rep(cas,1,T) {
            mes(mat, 0);
            scanf("%d", &n);
            rep(i,1,n) scanf("%lld", &a[i]);
            printf("Case #%d:
    %d
    ", cas, solve());
        }
    
        return 0;
    }
    View Code

    C - Magic boy Bi Luo with his excited tree

    题意:给出一棵树,在每个点有a[i]的财宝,但每条边有v[i]的花费,问从每个结点出发可得到的最大钱财数ans[i]。

    tags:树形dp,好伤脑细胞的题。。码了好几发,莫名其妙的错,  待补

    把每个结点分为往儿子不回来v1、往儿子回来v2、往父亲不回来fv1、往父亲回来fv2。第一次dfs维护出v1和v2,第二次dfs维护出fv1和fv2,最后答案就是max(v1+fv2, v2+fv1)。 

    D - Danganronpa

    题意:n种礼物,每种有a[i]个。无穷多个学生的桌子排成一行,限制:1、每张桌子里要有一个神秘礼物和一个普通礼物;2、相邻的桌子里的普通礼物要不同;3、礼物要放在相邻的桌子里。    问最多有多少个学生拿到礼物。

    tags:思维题,稍微理一下 maxn和sum/2 就好。

    // D
    #include<bits/stdc++.h>
    using namespace std;
    #pragma comment(linker, "/STACK:102400000,102400000")
    #define rep(i,a,b) for (int i=a;i<=b;i++)
    #define per(i,b,a) for (int i=b;i>=a;i--)
    #define mes(a,b)  memset(a,b,sizeof(a))
    #define INF 0x3f3f3f3f
    typedef long long ll;
    const int N = 200005;
    
    int T, n, ai;
    int main()
    {
        scanf("%d", &T);
        rep(cas,1,T) {
            scanf("%d", &n);
            ll sum=0;
            int mx=0;
            rep(i,1,n) {
                scanf("%d", &ai);
                sum+=ai;
                mx=max(mx, ai);
            }
            ll ans;
            if(mx>sum/2) ans=min(((sum-mx)*2)+1, sum/2);
            else ans=sum/2;
            printf("Case #%d: %lld
    ", cas, ans);
        } 
    
        return 0;
    }
    View Code

    G - Mountain

    题意: n*m的网格,每个网格有一个高度。定义周围网格高度都比其大的网格为山谷。给出了网格中的山谷位置,网格高度可以是1~n*m,但网格高度互不相同,求有多少种方案。

    tags:bzoj 2669: [cqoi2012]局部极小值  原题   

    没搞懂,待补

    // CCPC  G
    const int maxn=1<<9, mod=772002;
    char c[26][26];
    int dp[26][maxn];    // dp[i][s]表示'X'的状态为s时,已经填数字填到了i的方案数
    int a[8]={-1,-1,-1,0,1,1,1,0};
    int b[8]={-1,0,1,1,1,0,-1,-1};
    int n,m;
    int id[26][26];    // id[i][j]表示第 i行 j列是第几个'X',或者是 0则不是'X' 
    int cnt[maxn];
    int nn[maxn];
    
    int cal(int p){    //走过了 p个'X' 
        int w=1<<p;
        for(int i=0;i<w;i++){
            cnt[i]=0;
            for(int j=0;j<n;j++){
                for(int f=0;f<m;f++){
                    if(c[j][f]=='.'){    // 
                        int flag=0;
                        for(int u=0;u<8;u++){
                            int x=j+a[u];
                            int y=f+b[u];
                            if(x>=0&&x<n&&y>=0&&y<m){
                                if(c[x][y]=='X'&&(i&(1<<id[x][y]))==0){
                                    flag=1;    //不产生贡献 
                                    break;
                                }
                            }
                        }
                        cnt[i]+=flag^1;
                    }
                }
            }
        }
        dp[0][0]=1;
        for(int i=1;i<=n*m;i++) {
            for(int j=0;j<w;j++) {
                dp[i][j]=((LL)dp[i-1][j]*(cnt[j]-(i-1-nn[j])))%mod;        // 
                for(int q=0;q<p;q++) {
                    if(j&(1<<q)) {
                        dp[i][j]+=dp[i-1][j^(1<<q)];    //
                        dp[i][j]%=mod;
                    }
                }
            }
        }
        return dp[n*m][w-1];
    }
    int dfs(int x,int y,int p){    // 从(x,y)开始,有 t个'.'变为了'X',走过了p个'X'
        if(x==n){
            if(t%2==0){
                return cal(p);
            }
            else return (mod-cal(p))%mod;
        }
        if(y==m) return dfs(x+1,0,p);    //换行
        if(c[x][y]=='X'){
            id[x][y]=p;
            return dfs(x,y+1,p+1); //遍历过一个'X',p加一 
        }
        int flag=0;
        for(int i=0;i<8;i++){
            int x1=x+a[i];
            int y1=y+b[i];
            if(x1>=0&&x1<n&&y1>=0&&y1<m&&c[x1][y1]=='X'){
                flag=1;
            }
        }
        int ans=dfs(x,y+1,p);
        if(flag==0){
            id[x][y]=p;
            c[x][y]='X';    //把'.'变为'X'
            ans+=dfs(x,y+1,p+1);    // +=
            ans%=mod;
            c[x][y]='.';    //再变回 
            id[x][y]=0;
        }
        return ans;
    }
    int main()
    {
        int N=0;
        for(int i=1;i<maxn;i++) nn[i]=nn[i&(i-1)]+1;
        while (scanf("%d%d",&n,&m)!=EOF) {
            for(int i=0;i<n;i++) scanf("%s",c[i]);
            int flag=0;
            for(int i=0;i<n;i++){
                for(int j=0;j<m;j++){
                    if(c[i][j]=='X'){
                        for(int f=0;f<8;f++){
                            int x=i+a[f];
                            int y=j+b[f];
                            if(x>=0&&x<n&&y>=0&&y<m){
                                if(c[x][y]=='X'){
                                    flag=1;
                                }
                            }
                        }
                    }
                }
            }
            printf("Case #%d: ",++N);
            if(flag) printf("0
    ");
            else{
                printf("%d
    ",dfs(0,0,0));
            }
        }
    }
    View Code

    H - Special Tetrahedron

    题意:三维空间给出n 个点。定义特殊四面体:1、至少有4条相同长度的边;2、如果恰好有4条相同长度的边,另两条边要不相邻。 问有多少个特殊四面体。

    tags:看了大神题解码的,转送门

    思路:可以看成是求空间四边形,暴力枚举对角线。 对于每一条对角线,再枚举,把到两端点距离相等的点加到集合里。然后再枚举,看集合里任意两个点是否与两个端点共面。    最后去重,正四面体算了6次,两条边不等的算了2次。  时间复杂度O(n^4),但实际上,两个点与对角线两端点共面的情况只能在对角线中垂线上,不可能每次都有很多点在中垂线上,所以均摊下来时间复杂度并不高。

    #include<bits/stdc++.h>
    using namespace std;
    #pragma comment(linker, "/STACK:102400000,102400000")
    #define rep(i,a,b) for (int i=a;i<=b;i++)
    #define per(i,b,a) for (int i=b;i>=a;i--)
    #define mes(a,b)  memset(a,b,sizeof(a))
    #define INF 0x3f3f3f3f
    typedef long long ll;
    const int N = 210;
    
    struct Point {
        double x, y, z;
        Point operator - (const Point &b) const {
            return Point{b.x-x, b.y-y, b.z-z};
        }
    }p[N];
    double dis(Point a, Point b) {
        return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) + (a.z-b.z)*(a.z-b.z));
    }
    Point xmul(Point a, Point b) {    //叉积 
        return Point{a.y*b.z-b.y*a.z, a.z*b.x-b.z*a.x, a.x*b.y-b.x*a.y};
    }
    double dmul(Point a, Point b) {    //返回 0则两向量垂直 
        return a.x*b.x+a.y*b.y+a.z*b.z;
    }
    bool isgm(Point a, Point b, Point c, Point d) {    //返回 0则四点花线 
        if(dmul(xmul(a-b,a-c), a-d)) return false;
        return true;
    }
    
    int n; 
    vector<Point > ve;
    int solve()
    {
        int ans1=0, ans2=0;
        rep(ca,1,n) rep(cb,ca+1,n) if(ca!=cb) {    //枚举对角线 
            ve.clear();
            rep(i,1,n) if(i!=ca && i!=cb) {
                if(dis(p[i],p[ca])==dis(p[i],p[cb])) ve.push_back(p[i]);
            }
            int sz=ve.size(); 
            rep(i,0,sz-1) {
                double dis1=dis(ve[i],p[ca]); 
                rep(j,i+1,sz-1) if(dis(ve[j],p[ca])==dis1) {
                    if(isgm(p[ca],p[cb],ve[i],ve[j]))  continue;
                    if(dis(ve[i],ve[j])==dis1 && dis(p[ca],p[cb])==dis1) ans2++;
                    else ans1++;
                }
            }
        } 
        ans1/=2, ans1=ans1+ans2/6;
        return ans1;
    }
    int main()
    {
        int T;  scanf("%d", &T);
        rep(cas,1,T) {
            scanf("%d", &n);
            rep(i,1,n) scanf("%lf %lf %lf", &p[i].x, &p[i].y, &p[i].z);
            printf("Case #%d: %d
    ", cas, solve());
        }
    
        return 0;
    }
    View Code

    K - Lweb and String

    题意:一个字符串,它的字符集合里,求它能够形成的最长递增子序列长度。

    tags: SB题     被坑了,看懂题意,感觉题目反常就多看几遍 ==

    #include<bits/stdc++.h>
    using namespace std;
    #pragma comment(linker, "/STACK:102400000,102400000")
    #define rep(i,a,b) for (int i=a;i<=b;i++)
    #define per(i,b,a) for (int i=b;i>=a;i--)
    #define mes(a,b)  memset(a,b,sizeof(a))
    #define INF 0x3f3f3f3f
    typedef long long ll;
    const int N = 200005;
    
    int vis[200];
    string s;
    int main()
    {
        int T;
        scanf("%d", &T);
        rep(cas,1,T) {
            cin>>s;
            int len=s.size();
            mes(vis,0);
            rep(i,0,len-1) vis[s[i]]=1;
            int ans=0;
            rep(i,0,199) if(vis[i]) ans++;
            printf("Case #%d: %d
    ", cas, ans);
        }
    
        return 0;
    }
    View Code
  • 相关阅读:
    查询OOP课程
    MySchool
    华为机试——字符倒叙输出
    数组的sizeof
    华为机试——统计排序
    华为机试——整理数字字符串
    华为机试——求最小张数换零钱算法
    华为机试——拼写检查程序
    C++中的bitset
    华为机试——数字字符串转二进制
  • 原文地址:https://www.cnblogs.com/sbfhy/p/6622325.html
Copyright © 2011-2022 走看看