上午
考试啊,也是根本做不出来。先看了T1,是道简单的树形dp(不会做法假了吧)。T2就直接给我创飞,只能打个 分暴力(正解似乎是dp?)。然后,也是头昏脑涨,便睡趴了十五分钟,然后再来想T2。想的实在是没有头绪,就来写日记,给脑子初始化一下,再去想了。今天这个考试,标题是C组,然而题目标题是B组。
表里不一的模拟赛链接 看不太懂的题解
T1:给你一个森林,然后让你求每颗树的直径和。
是个学过树形dp的人都会打吧。 就打出来。但是我去上厕所的时候,看见 yzp 调了半个小时多,不会又 RE 了吧
T2:定义一个子区间的价值是整个子区间里所有的数的 乘上子区间里所有数的和。
先想到想到了线段树维护区间和和区间 ,然后 暴力,总时间复杂度 。然后过了 又想到了 的做法,也算是一大进步,优化掉了一个 。
T3:求众数出现次数大于区间长度的一半的子区间个数。
打一个 暴力,能拿 分(蚊子肉也是肉嘛)
T4:暴力都不想打(每次都这样)
算了,还是试试T4的暴力吧。
总结:还是 大菜特菜 ,还得练(还得练到什么时候去啊)
然后就考完了,反方向的rank4,退役得了。
然后,熊老师说发题解,然而并没有(皇帝的新题解),于是我就去做dp了,我的dp实在太菜了。
我什么时候能再场切一道蓝题呢?
下午
也是改题。那个私人题解,给的是部分分题解也不说一声,害得我浪费了半个小时,在讲T2前极限改出来。
T1:场切不说。
T2:source
先说那个题解给的私人部分分做法:设值域的上界是 , 为 到 的区间 ,则 至多有 个取值。 枚举每一个右端点 ,设在 的 个取值里,有一个取值为 ,则对于每一个 ,二分查找最靠左的左端点 使得 。时间复杂度 。
再说正解:在那 个 的取值里,我们发现对于每个取值,加一个之后,它就会变成它和 的 。这样我们就可以 更新每一个 。时间复杂度
T3&T4:没改,晚上再说
晚上
先玩了一会原神(当然是下课玩的,上课没有),然后看到方大三抽出林尼,欸这您受得了吗。
T4:source solution
这个题目解法用文字描述实在是太繁琐了(其实是我不想写,也不会写),于是把代码打在这里好了。
#include<bits/extc++.h>#define int long longusing namespace std;const int maxn = 65;const int mod = 1e9 + 7;int n,m,k;int head[maxn],idx;int f[maxn][10],dep[maxn];int fa[maxn],l[20];int c[20],d[20];struct edge{ int v,nxt; edge(int v = 0,int nxt = 0):v(v),nxt(nxt){};}e[maxn << 1];void adde(int u,int v){ e[++idx] = edge(v,head[u]); head[u] = idx;}int binpow(int x,int y)//快速幂{ int ret = 1; while (y) { if (y & 1) ret = ret * x % mod; x = x * x % mod; y >>= 1; } return ret;}void dfs(int u,int fa)//lca预处理{ f[u][0] = fa; dep[u] = dep[fa] + 1; for (int i = 1; i < 10; i++) f[u][i] = f[f[u][i - 1]][i - 1]; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].v; if (v == fa) continue; dfs(v,u); }}int lca(int u,int v)//最近公共祖先{ if (dep[u] < dep[v]) swap(u,v); for (int i = 9; i >= 0; i--) if (dep[u] - (1 << i) >= dep[v]) u = f[u][i]; if (u == v) return u; for (int i = 9; i >= 0; i--) if (f[u][i] != f[v][i]) u = f[u][i],v = f[v][i]; return f[u][0];}void init()//并查集初始化{ for (int i = 1; i <= n; i++) fa[i] = i;}int find(int x)//并查集查找{ if (fa[x] == x) return x; return fa[x] = find(fa[x]);}signed main(){ scanf("%lld%lld%lld",&n,&m,&k); for (int i = 1; i < n; i ++) { int u,v; scanf("%lld%lld",&u,&v); adde(u,v); adde(v,u); } dfs(1,0); for (int i = 1; i <= m; i++) { scanf("%lld%lld",c + i,d + i); l[i] = lca(c[i],d[i]);//预处理lca } int ans = binpow(k,n - 1),g,sum; int tmp1,tmp2,st,en; for (int i = 1; i < (1 << m); i++) { g = 1,sum = 0; init(); for (int j = 0; j < m; j++) { if (i & (1 << j)) { g = -g;//容斥的正负性 st = c[j + 1];//从一个点到lca while (st != l[j + 1]) { if (f[st][0] == l[j + 1]) break; tmp1 = find(st),tmp2 = find(f[st][0]); if (tmp1 != tmp2) fa[tmp1] = tmp2;//合并连通块 st = f[st][0];//往上跳 } en = d[j + 1];//从另一个点到lca while (en != l[j + 1]) { if (f[en][0] == l[j + 1]) break; tmp1 = find(en),tmp2 = find(f[en][0]); if (tmp1 != tmp2) fa[tmp1] = tmp2;//合并连通块 en = f[en][0];//往上跳 } if (st != l[j + 1] && en != l[j + 1]) { tmp1 = find(st); tmp2 = find(en); if (tmp1 != tmp2) fa[tmp1] = tmp2; }//如果都不是lca就把两个点合并 } } for (int j = 2; j <= n; j++) if (fa[j] == j) sum ++;//计数连通快 ans = (ans + g * binpow(k,sum) % mod + mod) % mod;//记录答案 } ans = (ans + mod) % mod; cout << ans; return 0;}打完了它还 WA 了一次,死因:没开 long long (虽然这也不是第一次)。
然后就只剩 分钟了,切点水题吧。
