这场比赛特点在于两个简单题太坑,严重影响了心情。。导致最后只做出两题....当然也反映出心理素质的重要性
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; }
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; }
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 }
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; }
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; }