
日记
上午
考试啊,也是根本做不出来。先看了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
这个题目解法用文字描述实在是太繁琐了(其实是我不想写,也不会写),于是把代码打在这里好了。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
#include<bits/extc++.h>
#define int long long
using 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
(虽然这也不是第一次)。
然后就只剩 分钟了,切点水题吧。