2016 CCPC 网络赛 B 高斯消元 C 树形dp(待补) G 状压dp+容斥(待补) H 计算几何
A - A water problem 水题,但读题有个坑,输入数字长度很大。。
题意:给出n个数(给出的每个数的质因子最大不超过2000),选出多个数相乘得b。问有多少种选法让b 为完全平方数。
tags:高斯消元,求异或方程组解的个数。 好题
每个数先素数分解开。 对于2000以内的每个素数p[i],这n个数有奇数个p[i]则系数为1,偶数个则系数为0,最后n个数的p[i]系数异或和都要为0才会使得最后的积为完全平方数。 所以,构建出系数矩阵,然后高斯消元,解出异或方程组的秩rank,自由变元的数量即为 x=n-rank,最后解的个数为2^x,但要注意减去全部为0的一种情况。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }
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)。
题意:n种礼物,每种有a[i]个。无穷多个学生的桌子排成一行,限制:1、每张桌子里要有一个神秘礼物和一个普通礼物;2、相邻的桌子里的普通礼物要不同;3、礼物要放在相邻的桌子里。 问最多有多少个学生拿到礼物。
tags:思维题,稍微理一下 maxn和sum/2 就好。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// 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; }
题意: n*m的网格,每个网格有一个高度。定义周围网格高度都比其大的网格为山谷。给出了网格中的山谷位置,网格高度可以是1~n*m,但网格高度互不相同,求有多少种方案。
tags:bzoj 2669: [cqoi2012]局部极小值 原题
没搞懂,待补
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// 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)); } } }
题意:三维空间给出n 个点。定义特殊四面体:1、至少有4条相同长度的边;2、如果恰好有4条相同长度的边,另两条边要不相邻。 问有多少个特殊四面体。
tags:看了大神题解码的,转送门
思路:可以看成是求空间四边形,暴力枚举对角线。 对于每一条对角线,再枚举,把到两端点距离相等的点加到集合里。然后再枚举,看集合里任意两个点是否与两个端点共面。 最后去重,正四面体算了6次,两条边不等的算了2次。 时间复杂度O(n^4),但实际上,两个点与对角线两端点共面的情况只能在对角线中垂线上,不可能每次都有很多点在中垂线上,所以均摊下来时间复杂度并不高。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }
题意:一个字符串,它的字符集合里,求它能够形成的最长递增子序列长度。
tags: SB题 被坑了,看懂题意,感觉题目反常就多看几遍 ==
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }