清北学堂国庆day2解题报告
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了清北学堂国庆day2解题报告相关的知识,希望对你有一定的参考价值。
Day2解题报告
张炳琪
时间安排::
T1:跑了三十多分钟打的表,同时做的后面的题
T2:剩余时间一直在做
T3:没写
答题情况和错误分析::
T1:数论不会推,只能打暴力打表了60分
T2:写了二百多行的树的相关代码,考虑了互相选子树的情况,但是代码实现能力欠缺10分
T3:没看懂题目没写 0分
题目解析:
T1:大概是讲式子转化成 a * b * c == n 的情况 然后强制令a 《= b <= c,然后枚举a b统计c的个数,a枚举三次根号n,b枚举到二次根号n大约是十的八次方的复杂度
T2:
做法流程如下
首先对于每个询问倍增求lca,然后找出距离长度,根据两人走路挑最优的来考虑,两人肯定会相向而行,则肯定会有一个交点,在这个交点上的其他子树,肯定是两人轮流挑子树和最大的子树先行。故进行排序,相隔而挑。
用两个数组分别记录每个节点的最优选择和次优选择
然后各种子树和进行维护交点的取值,重要的就是去处理交点的分叉
T3:
九维dp,输入数据后首先处理完找出什么都不加的最小值,然后同时找出在中间添加某个数字的得到的价值
dp[31][6][6][6][6][2][2][2][2]来存储状态
第一维代表选取的数字
2~5维表示四个方块选取的数量,6 ~ 9维表示四个方块是否选取了这个数,之后dp的转移就是数个数
然而我证明不出来无后效性的正确性
代码:
T1:
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; long long n; long long ans,tmp; int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); cin >> n; for (long long i = 1,v;i * i <= (v = n / i);i++,ans++) for (long long j = i + 1;j * j <= v;j++) ans +=n / (i * j) - j; // 到这里计算的是三个数字不重复的个数 ans += tmp * 6; tmp = 0; for (long long i = 1,v;(v = i * i)<= n;i++) { tmp += n / v; if (i * i <= n / i) tmp--; } ans += tmp * 3;// 对重复的进行计算 cout << ans << endl; return 0; }
T2:
#include<queue> #include<cstdio> #include<algorithm> #include<iostream> #define MAXN 100100 #include<vector> using namespace std; struct Edge{ int vi;int vj;int wei;int next_; }edge[MAXN * 4]; int n,m,now; int head[MAXN];int deep[MAXN];int fd[MAXN];int fa[MAXN][20];int start[MAXN];int end[MAXN];int ver[MAXN]; int sum[MAXN * 5][2];// 储存两种优选方案 int z[MAXN << 2]; int q[MAXN]; void push(int vi,int vj,int wei) { now++; edge[now].vi = vi; edge[now].vj = vj; edge[now].wei = wei; edge[now].next_ = head[vi]; head[vi] = now; } int get(int p,int d) { if(d == -1)return p; int x = 0; while(d) { if(d & 1)p = fa[p][x]; d >>= 1; x++; } return p; } int find(int p1,int p2) { if(deep[p1] < deep[p2])swap(p1,p2); p1 = get(p1,deep[p1] - deep[p2]); int x = 0; while(p1 != p2) { if(!x || fa[p1][x] != fa[p2][x]) { p1 = fa[p1][x]; p2 = fa[p2][x]; x++; } else x--; } return p1; } int calc(int p1,int p2) { if(p1 == fa[p2][0])return ver[1] - ver[p2]; else return ver[p1] + fd[p1]; } int calcp(int p,int v) { int l = start[p] - 1,r = end[p]; while(l + 1 != r) { int m = (l + r) >> 1; if(v > z[m])l = m; else r = m; } return r; } int main() { freopen("b.in","r",stdin);freopen("b.out","w",stdout); scanf("%d%d",&n,&m); int tot = 0; for(int i = 1;i < n;i++) { int vi,vj,wei; scanf("%d%d%d",&vi,&vj,&wei); tot += wei; push(vi,vj,wei); push(vj,vi,wei); } q[1] = 1; deep[1] = 1; int front = 1,tail = 1; while(front <= tail) { int noww = q[front++]; for(int i = head[noww];i;i = edge[i].next_) { int vj = edge[i].vj; if(deep[vj])continue; deep[vj] = deep[noww] + 1; fd[vj] = edge[i].wei; fa[vj][0] = noww; int p = noww,x = 0; while(fa[p][x]) { fa[vj][x + 1] = fa[p][x]; p = fa[p][x]; x++; } q[++tail] = vj; } } int cnt = 0; for(int i = n;i >= 1;i--)//队列里面的倒序,从深到浅 { int noww = q[i]; start[noww] = cnt + 1; for(int j = head[noww];j;j = edge[j].next_) { int vj = edge[j].vj; if(deep[vj] == deep[noww] + 1) { z[++cnt] = ver[vj] + edge[j].wei; ver[noww] += ver[vj] + edge[j].wei; } } z[++cnt] = tot - ver[noww]; end[noww] = cnt; sort(z + start[noww],z + end[noww] + 1); sum[end[noww]][0] = z[end[noww]]; sum[end[noww]][1] = 0; for(int j = end[noww] - 1;j >= start[noww];j--) { sum[j][0] = sum[j + 1][0]; sum[j][1] = sum[j + 1][1]; if((j & 1) == (end[noww] & 1))sum[j][0] += z[j]; else sum[j][1] += z[j]; } cnt++; } for(int i = 1;i <= m;i++) { int vi,vj; scanf("%d%d",&vi,&vj); int lca = find(vi,vj); int dist = deep[vi] + deep[vj] - 2 * deep[lca]; int dista = (dist + 1) / 2; int px,px1,px2; if(deep[vi] - deep[lca] < dista)// 交点在第二个人到lca的路径上 px = get(vj,dist - dista); else px = get(vi,dista); if(deep[vi] - deep[lca] < dista - 1)px1 = get(vj,dist - dista + 1); else px1 = get(vi,dista - 1); if(deep[vj] - deep[lca] < dist - dista - 1)px2 = get(vi,dista + 1); else px2 = get(vj,dist - dista - 1); int ans = 0; if(vi == px) { if(vj == px)ans = sum[start[px]][0]; else { int v2 = calc(px2,px); int p = calcp(px,v2); ans = sum[p + 1][0] + sum[start[px]][1] - sum[p][1]; } } else { if(vj == px) { int v1 = calc(px1,px); int p = calcp(px,v1); ans = v1 + sum[p + 1][1] + sum[start[px]][0] - sum[p][0]; } else { int v1 = calc(px1,px); int pp1 = calcp(px,v1); int v2 = calc(px2,px); int pp2 = calcp(px,v2); if(pp2 == pp1)pp2++; if(pp1 > pp2)swap(pp1,pp2); ans = v1 + sum[pp2 + 1][dist & 1] + sum[pp1 + 1][1 - (dist & 1)] - sum[pp2][1 - (dist & 1)] + sum[start[px]][dist & 1] - sum[pp1][dist & 1]; } } printf("%d\n",ans); } return 0; }
T3:
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #define dis(a,b,c,d) (abs(a-c)+abs(b-d)) #define INF 0x3f3f3f3f using namespace std; int A,B,C,D,E; int top[7][7];int note[7][7][6];int delta[10][10][40]; bool visited[10][10][10][10]; int dp[31][6][6][6][6][2][2][2][2]; int read() { int num = 0; char c = getchar(); while(c < ‘0‘ || c > ‘9‘)c = getchar(); while(c >= ‘0‘ && c <= ‘9‘ ) { num *= 10; num += c - ‘0‘; c = getchar(); } return num; } int main() { cin >> A >> B >> C >> D >> E; getchar(); for(int i = 0;i < 6;i++) { for(int j = 0;j < 6;j++) { top[i][j] = read(); for(int z = 1;z <= top[i][j];z++) note[i][j][z] = read(); } } int tot = 0; for(int i = 0;i < 6;i++)///以下求出不含中间部分的基础值(无法更改的部分) for(int j = 0;j < 6;j++) { if((i == 2 || i == 3 )&& (j == 2 || j == 3))continue; sort(note[i][j] + 1,note[i][j] + top[i][j] + 1); for(int op = 1;op < top[i][j];op++) if(note[i][j][op] + 1 == note[i][j][op + 1])tot += A;//情况A /* 这个地方用来预处理中间的那四个格子放那些数字会从周围的数字得到多少 贡献值 */ for(int c = 2;c <= 3;c++) for(int d = 2;d <= 3;d++) { if(dis(i,j,c,d) == 1)//距离为1 { for(int op = 1;op <= top[i][j];op++) { int zz = note[i][i][op]; delta[c][d][zz] += B; delta[c][d][zz + 1] += C; delta[c][d][zz - 1] += C; } } if(dis(i,j,c,d) == 2) { for(int op = 1;op <= top[i][j];op++) { int zz = note[i][j][op]; delta[c][d][zz] += D; delta[c][d][zz + 1] += E; delta[c][d][zz - 1] += E; } } } /*然后处理不是中间块提供的贡献值*/ for(int c = 0;c < 6;c++) for(int d = 0;d < 6;d++) { if((c == 2 || c == 3 )&& (d == 2 || d == 3))continue; if(visited[i][j][c][d] || dis(i,j,c,d) > 2)continue; if(c == i && d == j)continue; visited[i][j][c][d] = visited[c][d][i][j] = true; int dist = dis(i,j,c,d); for(int e = 1;e <= top[i][j];e++) for(int f = 1;f <= top[c][d];f++) { if(abs(note[i][j][e] - note[c][d][f]) == 0)tot += dist == 1 ? B : D; if(abs(note[i][j][e] - note[c][d][f]) == 1)tot += dist == 1 ? C : E; } } } memset(dp,0x3f,sizeof(dp));dp[0][0][0][0][0][0][0][0][0] = tot; int ans = INF; for (int a=0;a<30;a++) for (int b=0;b<=top[2][2];b++) for (int c=0;c<=top[2][3];c++) for (int d=0;d<=top[3][2];d++) for (int e=0;e<=top[3][3];e++) for (int s1=0;s1<=1;s1++) for (int s2=0;s2<=1;s2++) for (int s3=0;s3<=1;s3++) for (int s4=0;s4<=1;s4++) if (dp[a][b][c][d][e][s1][s2][s3][s4]!=INF) { int v=dp[a][b][c][d][e][s1][s2][s3][s4]; for (int sx1=0;sx1<=(b!=top[2][2]);sx1++) for (int sx2=0;sx2<=(c!=top[2][3]);sx2++) for (int sx3=0;sx3<=(d!=top[3][2]);sx3++) for (int sx4=0;sx4<=(e!=top[3][3]);sx4++) { int wmt=0; if (sx1) // { wmt+=delta[2][2][a+1]; if (s1) wmt+=A; if (s2) wmt+=C; if (s3) wmt+=C; if (s4) wmt+=E; } if (sx2) { wmt+=delta[2][3][a+1]; if (s1) wmt+=C; if (s2) wmt+=A; if (s3) wmt+=E; if (s4) wmt+=C; } if (sx3) { wmt+=delta[3][2][a+1]; if (s1) wmt+=C; if (s2) wmt+=E; if (s3) wmt+=A; if (s4) wmt+=C; } if (sx4) { wmt+=delta[3][3][a+1]; if (s1) wmt+=E; if (s2) wmt+=C; if (s3) wmt+=C; if (s4) wmt+=A; } if (sx1 && sx2) wmt+=B; if (sx1 && sx3) wmt+=B; if (sx1 && sx4) wmt+=D; if (sx2 && sx3) wmt+=D; if (sx2 && sx4) wmt+=B; if (sx3 && sx4) wmt+=B; int &t=dp[a+1][b+sx1][c+sx2][d+sx3][e+sx4][sx1][sx2][sx3][sx4]; if (t>v+wmt) t=v+wmt; } ans = min(ans,dp[30][top[2][2]][top[2][3]][top[3][2]][top[3][3]][s1][s2][s3][s4]); } printf("%d",ans); return 0; } /* 5 4 3 2 1 [1:1][1:2][1:3][1:4][1:5][1:6] [1:1][1:2][1:3][1:4][1:5][1:6] [1:1][1:2][5:1,2,3,4,5][5:1,2,3,4,5][1:5][1:6] [1:1][1:2][5:1,2,3,4,5][5:1,2,3,4,5][1:5][1:6] [1:1][1:2][1:3][1:4][1:5][1:6] [1:1][1:2][1:3][1:4][1:5][1:6] */
以上是关于清北学堂国庆day2解题报告的主要内容,如果未能解决你的问题,请参考以下文章