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

Posted 掉血菜鸡煮熟中

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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:\\n%d\\n", 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\\n", 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\\n");
        else{
            printf("%d\\n",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\\n", 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\\n", cas, ans);
    }

    return 0;
}
View Code

以上是关于2016 CCPC 网络赛 B 高斯消元 C 树形dp(待补) G 状压dp+容斥(待补) H 计算几何的主要内容,如果未能解决你的问题,请参考以下文章

CCPC2016合肥现场赛

2019CCPC网络赛 HDU 6702——找规律

2017湘潭赛 A题 Determinant (高斯消元取模)

2016CCPC东北地区大学生程序设计竞赛 - 重现赛 1003

2018 CCPC网络赛1004 - 费马大定理&数学

HDU6706 CCPC 2019网络赛 huntian oy 推式子+杜教筛