模拟赛总结
//https://images.cnblogs.com/cnblogs_com/blogs/769737/galleries/2232265/o_221024235906_12.jpg
//https://images.cnblogs.com/cnblogs_com/blogs/769737/galleries/2232265/o_221024123148_10.jpg
2024.2.6
T1 珠子
小 F 有
暴力:前缀和处理前
期望:
没用前缀和:
正解:双指针
先固定左端点,然后分别去找满足颜色要求的最小
至于怎么较快的找到不会先咕咕
T2 数组
初始
-
:已知 ,修改所有数, -
:已知 ,将 改为
给出若干操作,在每次操作后输出数组元素和
第一个好办,看第二种
第二种的麻烦点在于上一次
如果
否则减去上次
for(int i = 1;i <= m;i++)
{
cin >> op;
if(op == 'A')
{
cin >> t[i].p >> t[i].q;
cnt = i;//记录最新A操作的时候
sumq = 0;//覆盖掉
cout << (ll)t[i].p * sum + t[i].q * n << '\n';
}
if(op == 'B')
{
cin >> t[i].p >> t[i].q;
if(tag[t[i].p] <= cnt)//上次该处的B已被覆盖
{
sumq = sumq + (ll)t[i].q - (ll)(t[cnt].p * t[i].p + t[cnt].q);// 减去A操作留下的值(p*i+q),加上y
tag[t[i].p] = i;
}
else
{
int y = tag[t[i].p];
sumq = sumq + t[i].q - t[y].q;//减去上次B操作留下的值
tag[t[i].p] = i;
}
cout << (ll)t[cnt].p * sum + n * t[cnt].q + sumq << '\n';
}
}
蒟蒻没有考虑到
T3 幸运区间
已知
暴力:分解质因数,拿质因子搞
正解:双指针(byd)
考虑一个显然的结论:若一个序列存在一个子序列,其所有数的
那么,每次固定左端点,找到最小的
至于找到最小的
还有一点:有结论我们还可推得:一个序列的
int l=1,r=1;
for(l=1;l<=n;l++)
{
r=max(l,r);//双指针思想,性质保证了可以固定r跳l,这样就是O(n)
while(l<=r&&r<=n)
{
if(st.getgcd(l,r,1,n,1)==1) break;//此处使用线段树,由结论可知gcd有传递性
r++;
}
ans+=n-r+1;
}
T4 找不同
已知一串单词,给定若干区间,判断每个区间中是否有重复单词
先Hash,此处使用map开的mp和last
类比HH的项链,不同单词即不同颜色,那么没有重复就是区间内颜色种类等于区间长度
蒟蒻没想到类比,打的暴力,又gg了,只能说树状数组那章是划水过来的
for(int i = 1;i <= q;i++)
{
int l1 = b[i].l;
int r1 = b[i].r;
while(pos <= r1)
{
add(pos,1);
if(last[mp[pos]] == 0) last[mp[pos]] = pos;//记录上一次出现位置
else
{
if(last[mp[pos]] < pos)//上一次出现位置在区间内
{
add(last[mp[pos]],-1);//把原来加的1减掉
last[mp[pos]] = pos;//更新
}
}
pos++;
}
ans[b[i].id].val = sum(r1) - sum(l1 - 1);
ans[b[i].id].l = l1;
ans[b[i].id].r = r1;
}
两道双指针题给蒟蒻的第一感觉就是:
2024.2.18
T1 家庭作业
设给的数为
对于
为了防止枚举时重复计算答案,每求完一次
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
int k = __gcd(a[i],b[j]);
if(k != 1)
{
ans = ans * k % mod;
a[i] /= k;
b[j] /= k;//除掉,防止重复
}
}
}
硬拿
本来想到
还是太蒟蒻了
T2 距离之和
md调了半天发现是指令数组只开了
对于一次移动(以向右为例),向右一步,首先不影响
其他同理
开两个权值线段树(横、纵坐标)统计点个数可过,但是考场上内存炸了直接gg
就挺烦人
其他:
我们可以把坐标排序,用二分找出发点所在分界,这样就免去了桶
//向右
int num = lower_bound(x + 1,x + n + 1,dx + 1) - x;
num = n - num + 1;//lower_bound返回的是第一个不小于dx + 1的数的位置,具体个数要处理一下
int res = n - num;
sum += res - num;//res是左边的点,距离+1,num是右边的点,距离-1
dx++;//更新坐标
或者每次以机器人位置为原点建系,把点划分成在
仍以向右为例,此时上下部分不影响,在左边和在
//向右
dx++;
sum += ony + zuo - you;//更新
zuo += ony;//出发点所在列上的点在新Y轴的左边
ony = px[dx];//新y轴上的点
you -= ony;//右边在dy列的点到了坐标轴上,不属于右边
T3 country
哈希不用说
(但正解是kmp)
按样例顺序遍历处理有
听说暴力展开大写字符有
乱序数据的话要记忆化搜索
坑点在于两个大写代表的串拼起来后可能会生成目标串
首先处理出模式串的
匹配完
dfs:
void dfs(int x,int pos)
{
cout << 114514 << endl;
if(vis[x][pos]) return;
int j = pos;
for(int i = 1;i <= len[x];i++)
{
if(a[x][i] >= 'A' && a[x][i] <= 'Z')
{
int v = a[x][i] - 'A';
dfs(v,j);//指针指到了大写字母,直接连着向后匹配
f[x][pos] = (f[x][pos] + f[v][j]) % mod;//大写部分的匹配方案数就是f[v][j],dfs后更新答案
j = g[v][j];//dfs完了,说明大写字母内部匹配完毕,更新指针
}
else
{
while(j && s[j + 1] != a[x][i]) j = fail[j];
if(s[j + 1] == a[x][i]) j++;
if(j == m) f[x][pos] = (f[x][pos] + 1) % mod,j = fail[j];
}//小写字母部分,KMP版子
}
vis[x][pos] = 1;
g[x][pos] = j;//标记并记录匹配完毕的位置
}
预处理
for(int i = 2,j = 0;i <= m;i++)
{
while(j && s[i] != s[j + 1]) j = fail[j];
if(s[i] == s[j + 1])j++;
fail[i] = j;
}//KMP板子预处理fail指针
T4 太空飞船
byd考场上直接想这题想歪了还忘了打暴力保底
暴力
还有一个
容斥原理
采用 “总数-非法的方案” (容斥是不是都爱这么玩)
如果
设
首先,
接下来就是容斥:减掉
还有一点:从质数去枚举不方便,不妨枚举
for(int i = 1;i <= n;i++)
{
for(int j = 2;j * j <= a[i];j++)
{
if(a[i] % j == 0)
{
num[j]++;
if(j * j != a[i]) num[a[i] / j]++;
}
}
}//处理num数组
ans = C[n];//总数
for(int i = 1;i < N;i++)
{
bool f = 0;
for(int j = 2;j * j <= i;j++)
{
if(i % j == 0 && i / j % j == 0)
{
f = 1;
break;
}//如果数的次数超过1
}
if(f) continue;
int cnt = 0;
int x = i;
for(int j = 2;j * j <= x;j++)
{
if(x % j == 0)
{
cnt++;
while(x % j == 0) x /= j;
}//统计质数个数
}
if(x != 1) cnt++;
if(cnt % 2 == 1) ans -= C[num[i]];
else ans += C[num[i]];//容斥
}
2024.2.19
T1 素数
md内存又开炸了 啊啊啊啊不对呀我记得开的没那么扯淡呀
欧拉筛出范围内所有素数,拿前缀和
for(int i = 1;i <= cnt;i++) sum[i] = sum[i - 1] + pri[i];
//cout << sum[cnt] << endl;
for(int i = 1;i <= cnt;i++)
for(int j = 1;j <= i;j++)
{
if(sum[i] - sum[j - 1] > 33000) continue;
num[sum[i] - sum[j - 1]]++;
}
我tm考场上怎么开了5e7wc
T2 晨练
dp
设
如果选择跑,那就是
如果不跑,那么直接休息
坑点:疲劳度为
初始化:
ok,考场上还好从坑里爬出来了
100pts
T3 奇怪的桌子
还有十分的小数据,手算的,好像算错了
找规律
考场上想到了一点
(考试时画的)
有一些列的点数是有规律的(有两个点的是
再看一个一列放三个点的
列也有规律,似乎是
那我们就猜测:
证明:
这一点的用处就在于:若某一列有
然后dp (啊?)
设
如果我们在当前列要放
说明:组合数的指数部分,结合一列三个点的图可以得到。即如果
但是有一些疑点:
一共
而且上面两幅图的总点数也是不一样的
?
!
我们观察到对于一种方案,每个正方形内部点的分布是一致的,比如第一张图中,正方形内都是一列
那么我们是不是可以用一个正方形内部的点分布来代替填满整个图,即一种点分布就是一种方案
可以,结合规律,我们就可以用正方形内部的列把其他列推出来
所以只需要dp一个正方形,答案就是
for(int i = 1;i <= n;i++)
for(int j = k - (n - i) * n;j <= k;j++)//这里j的下线意思是:还剩n-i列未填,这里面最多填(n-i)*n个点,那么前面至少填了k-(n-i)*n个点
for(int l = 0;l <= min(n,j);l++)
dp[i][j] = (dp[i][j] + dp[i - 1][j - l] * qpow(C(n,l),(m / n + (i <= m % n)) % (mod - 1)))% mod;
还没完
循环的三次方肯定砍不掉了,那就砍掉log,预处理快速幂
for(int i = 1;i <= n;i++)
for(int j = 0;j <= max(n,k);j++)
qp[i][j] = qpow(C(n,j),(m / n + (i <= m % n)) % (mod - 1)) % mod;
T4 学校
图的算法忘完了c
HXY算的时间就是最短路耗时
即求断掉某条边后是否还有和原先耗时相同的最短路
每次删边后跑
由于只考虑最短路,所以可以吧原图中所有最短路(可能不止一条)经过的边提出来建新图,如果在新图中删掉某条边后图不联通,那么删掉这条边就是找不到最短路的
就是
坑点:有重边
2024.2.21
T1 排序
TMD又没开long long
TMD又没开long long
TMD又没开long long
啊啊啊啊啊啊
正确性(排序不等式):
若
则
一张图解法(以
T2 牛吃草
想到了二分
二分
用类似贪心的方法(肯定是错的)尝试挂分,没想到挂出
正解还用到了
设
则
这个暴力式可得
但也有坑点:
for(int i = x;i <= n;i++)
{
dp[i] = dp[i - 1];//跳出坑点
for(int j = i - w[i];j <= i - x;j++)
dp[i] = max(dp[i],dp[j] + (i - j));
}
接下来观察到
说明
下限单调不降,相当于滑动窗口
for(int i = x;i <= n;i++)
{
//if(i <= w[i]) continue;
if(w[i] < x)//区间不存在,直接赋值跑路
{
dp[i] = dp[i - 1];
continue;
}
while(h <= t && dp[i - x] - i + x >= dp[q[t]] - q[t]) t--;
q[++t] = i - x;
while(h <= t && i - w[i] > q[h]) h++;
dp[i] = max(dp[i - 1],dp[q[h]] + (i - q[h]));
}
T3 树上的宝藏
想到的是
设
具体的,如果一个节点
但是直接
这一部分分的题解处理方法很巧妙:断掉特殊边,对形成的两棵子树分别
这样就避免了特殊边的干扰,非常容易的得到
对于特殊边连接的点
初始化:叶子结点不论状态值均为1
for(int j = 1;j <= n;j++) if(in[j] == 1) dp[j][1] = dp[j][0] = 1;
...
void dfs(int x,int fa)
{
ll ans = 1;
ll res = 1;
for(int i = head[x];i;i = e[i].next)
{
if(e[i].idx == cut) continue;//模拟断边操作
int k = e[i].to;
if(k != fa)
{
dfs(k,x);
ans = (ans * (dp[k][0] + dp[k][1])) % mod;
res = (res * (dp[k][0])) % mod;//求乘积
}
}
dp[x][0] = ans;
dp[x][1] = res;
}
正解太玄乎:
还是先忽略特殊边
设
其实就是选定一条边后需要用到连接点外部的信息
设节点
我们把
父亲节点的外部就是
抽象?来看图(
由于不考虑特殊边,所以这里的
类似的,可以得到
这样求得
设特殊边连接的是
记得逆元+膜
T4 MEX
大毒瘤,暴力一点分没有
假设当前求的MEX为
都要出现 不出现
枚举右端点,可知区间的左端点也在一个范围内
那么,我们就用
形式化的就是(
然后再把这个公式改一下
对每一个
然后作差就能得到答案
另一种理解:
把问题转化成求MEX
某区间的MEX
固定左端点,当右端点递增时,该区间的MEX显然单调,说明合法的右端点能组成一个集合,这个集合中的最大值就是
使用线段树维护
由于
然后就不会了
2024.2.22
T1 打赌
找规律
每四列一个周期,分成两部分,右边是不足四列的部分,左边的答案就是
接下来摇一摇色子就可得到:
算就行了
T2 舞会
方法一:权值线段树维护个数,匹配上一个后删点
删点打标记很麻烦,写炸了,
还有写平衡树的
正解:先排序保证单调,然后用类似双指针的方法,对于第
int l = 1;
int r = n;
while(l <= n && r >= 1 && b[l] < 0 && g[r] > 0)
{
if(-b[l] > g[r])
{
ans++;
l++;
r--;
}
else r--;
}//男比女高
l = 1,r = n;
while(l <= n && r >= 1 && g[l] < 0 && b[r] > 0)
{
if(-g[l] > b[r])
{
ans++;
l++;
r--;
}
else r--;
}//女比男高
T3 最小生成树
首先,由于一定存在一棵最小生成树,是
只有互质的两数之间的边才有可能被保留,否则生成树一定不是最小
想到这里了,然后就想着用组合
看到互质,还可以想到欧拉函数
注意到题目要求父节点标号小于子节点,而
利用乘法原理得到
T4 买汽水
状压暴力有
正解:折半搜索
直接搜复杂度是
void dfs1(int x,int sum)
{
if(x == n / 2 + 1) ans1[++tot] = sum;
else
for(int i = 0;i <= 1;i++)
dfs1(x + 1,sum + (i?a[x]:0));
}//dfs1(1,0)左半部分
void dfs2(int x,int sum)
{
if(x == n + 1) ans2[++num] = sum;
else
for(int i = 0;i <= 1;i++)
dfs2(x + 1,sum + (i?a[x]:0));
}//dfs2(n / 2 + 1,0)右半部分
int l = 1;
int r = num;
ll ans = -1;
while(l <= tot && r)
{
while(ans1[l] + ans2[r] > m) r--;//找到第一个符合条件的r
if(r) ans = max(ans,ans1[l] + ans2[r]);
l++;//排序后这样做只会使和增加,所以不用动右指针
}
2024.3.10
T1 [USACO09MAR] Cow Frisbee Team S
十来分钟打了个板子小代码就溜了
结果不出意外的出意外了——T了,
int n,f;
int a[N];
map<ll,int> dp;
int sum = 0;
int main()
{
scanf("%d%d",&n,&f);
for(int i = 1;i <= n;i++) scanf("%d",&a[i]),sum += a[i];
dp[0] = 1;
for(int i = 1;i <= n;i++)
{
for(int j = sum;j >= a[i];j--)
{
dp[j] = (dp[j] + dp[j - a[i]]) % mod;
}
}
ll ans = 0;
for(int i = f;i <= sum;i += f)
ans = (ans + dp[i]) % mod;
printf("%d",ans);
return 0;
}
后来经wzw奆神点拨才发现:
所以他要求队伍的总能力必须是
的倍数
这不是排列的trick吗?
所以把上面代码滚掉的一维(第几个)拿回来,开第二维表示余数,余数那一维走背包操作,答案就是
for(int i = 1;i <= n;i++) scanf("%d",&a[i]),dp[i][a[i] % f] = 1;//初始化
for(int i = 1;i <= n;i++)
{
for(int j = 0;j < f;j++)
{
dp[i][j] = (dp[i][j] + dp[i - 1][j]) % mod;
dp[i][j] = (dp[i][j] + dp[i - 1][((j - a[i]) % f + f) % f]) % mod;//内部取余操作是为了防止出现负数
}
}
printf("%d",dp[n][0]);
T2 Prufer 序列多源生成树
标题党
还以为是
printf("1");
wc
正解是什么树的直径,用搜索就行
题意要求在保证深度最小的情况下最小化根的编号
保证树的深度最小,可以计算树的直径并取得最小,在保证直径最小下令编号最小,用树上
//树形dp求直径
//dp[i]:以i为根的子树中最长路径长度
//g[i]:以i为根的子树中次长路径的长度
//id[i]:i对应的最长路径端点
void dfs1(int x,int fa)
{
for(int i = head[x];i;i = e[i].next)
{
int k = e[i].to;
if(k == fa) continue;
dfs1(k,x);
if(dp[x] < dp[k] + 1)
{
g[x] = dp[x];
dp[x] = dp[k] + 1;
id[x] = k;
}
else if(g[x] < dp[k] + 1) g[x] = dp[k] + 1;
}
}
//合并找直径
void dfs2(int x,int fa)
{
d[x] = d[fa] + 1;
if(x == id[fa]) d[x] = max(d[x],g[fa] + 1);
else d[x] = max(d[x],dp[fa] + 1);
for(int i = head[x];i;i = e[i].next)
{
int k = e[i].to;
if(k != fa) dfs2(k,x);
}
dp[x] = max(dp[x],d[x]);
}
int main()
{
int n;
scanf("%d",&n);
for(int i = 1;i <= n - 1;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
//printf("1");
dfs1(1,0);
dfs2(1,0);
int ans = 1;
for(int i = 1;i <= n;i++)//从小号开始枚举
if(dp[ans] > dp[i]) ans = i;
printf("%d",ans);
T3 P3216 [HNOI2011] 数学作业
前
ll check(ll x)
{
if(1 <= x && x <= 9) return 10;
if(10 <= x && x <= 99) return 100;
if(100 <= x && x <= 999) return 1000;
if(1000 <= x && x <= 9999) return 10000;
if(10000 <= x && x <= 99999) return 100000;
if(100000 <= x && x <= 999999) return 1000000;
if(1000000 <= x && x <= 9999999) return 10000000;
}
int main()
{
ll n,mod;
scanf("%lld%lld",&n,&mod);
ll ans = 0;
for(int i = 1;i <= n;i++)
{
ans = (ll)(ans * check(i) % mod + i % mod) % mod;//递推
}
printf("%lld",ans);
return 0;
}
上面的代码很暴力,但是有一点值得肯定:
找到了递推式,也就是
还是优化
矩阵快速幂
先用
完善
递归
这里
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,mod;
ll ten[20];
struct ma
{
ll m[5][5];
}ans,base;
ma cal(ma a,ma b)
{
ma tmp;
for(int i = 1;i <= 3;i++)
{
for(int j = 1;j <= 3;j++)
{
tmp.m[i][j] = 0;
for(int k = 1;k <= 3;k++) tmp.m[i][j] = (tmp.m[i][j] + a.m[i][k] % mod * b.m[k][j] % mod) % mod,tmp.m[i][j] = (tmp.m[i][j] % mod + mod) % mod;
//cout << tmp.m[1][1] << endl;
}
}
return tmp;
}
void init(int num)
{
ans.m[1][1] = ten[num] % mod;//记得取模
ans.m[1][2] = ans.m[2][2] = ans.m[2][3] = ans.m[3][3] = 1;
ans.m[1][3] = ans.m[2][1] = ans.m[3][1] = ans.m[3][2] = 0;
/*for(int i = 1;i <= 3;i++)
{
for(int j = 1;j <= 3;j++)
cout << ans.m[i][j] << " ";
cout << endl;
}*/
}//预处理左上角10的num次方
ma qpow(ll num,ll ci)
{
init(num);
base = ans;
while(ci)
{
if(ci & 1) ans = cal(ans,base);
base = cal(base,base);
ci >>= 1;
}
return ans;
}
int main()
{
scanf("%lld%lld",&n,&mod);
ten[0] = 1;
for(int i = 1;i <= 18;i++) ten[i] = ten[i - 1] * 10;//10的若干次方
int cnt = 0;
ll x = n;
while(x)
{
x /= 10;
cnt++;
}/最大位数
//cout << cnt << endl;
ma A;
A.m[1][1] = 0;
A.m[2][1] = 1;
A.m[3][1] = 1;
ma B;
B = qpow(1,8);
A = cal(B,A);//dp[0] ~ dp[9]
//cout << A.m[3][1] << endl;
for(int i = 2;i < cnt;i++)
{
B = qpow(i,9 * ten[i - 1] - 1);//9-99是90次,99-999是900次,以此类推
A = cal(B,A);
}//分区间处理
B = qpow(cnt,n - (ten[cnt - 1] - 1) - 1);//10的某次方到n
A = cal(B,A);//一定要尺寸对应,cal(A,B)就是错的
printf("%lld",A.m[1][1]);
return 0;
}
T4 P6239 [JXOI2012] 奇怪的道路
状压
维数定义的差不多,设
但这样开数组直接
后来发现:当前城市往后连(连标号比他大的)等价于后面的往前连(大编号连小编号),反之亦然。所以连的城市只需要考虑我原先想的那个区间的一半,这样的话空间也节约不少
再后来发现第二维状态需要修改,具体的,还是因为按原定义的话连的边的数量难以保证(我们只能从状态得知连没连),所以改进为
这样一来,
由于一对城市之间可以连若干条边,所以现在
由于区间重叠,所以直接
对于
改变奇偶的话,
坑点:
#include<bits/stdc++.h>
#define mod 1000000007
using namespace std;
int n,m,k;
int dp[35][2050][35];
int main()
{
scanf("%d%d%d",&n,&m,&k);
dp[2][0][0] = 1;//1 - 1 = 0无意义,直接从2开始
for(int i = 2;i <= n;i++)
{
int minn = max(1,i - k);//得有
for(int j = minn;j <= i - 1;j++)//枚举新增边的一个端点,另一端是i
{
for(int v = 1;v <= m;v++)
{
for(int s = 0;s <= (1 << (k + 1)) - 1;s++)
{
dp[i][s][v] = (dp[i][s][v] + dp[i][s ^ (1 << i - j) ^ 1][v - 1]) % mod;
}//i - j为枚举的点相对于最右端的位置,i的位置就是i - i = 0,所以是 1 << 0 = 1
}
}
for(int j = 0;j <= m;j++)
{
for(int s = 0;s <= (1 << k) - 1;s++)
{
dp[i + 1][s << 1][j] = (dp[i + 1][s << 1][j] + dp[i][s][j]) % mod;
}//区间平移
}
}
printf("%d",dp[n][0][m]);//答案就是点的度数都是偶数
return 0;
}
2024.3.24
cnm怎么全是数学cccc
T1 卫星照片
#include<bits/stdc++.h>
#define N 80
using namespace std;
int r,c;
char a[N][N];
int vis[N][N];
int cow,house;
int dx[5] = {0,0,0,1,-1};
int dy[5] = {0,1,-1,0,0};
int maxx,minx,maxy,miny;
int chang,kuan;
struct node
{
int x,y;
};
queue<node> q;
void bfs(int u,int v)
{
maxx = minx = u;
maxy = miny = v;
q.push({u,v});
vis[u][v] = 1;
while(!q.empty())
{
node tmp = q.front();
q.pop();
for(int i = 1;i <= 4;i++)
{
int nx = tmp.x + dx[i];
int ny = tmp.y + dy[i];
if(!vis[nx][ny] && nx >= 1 && nx <= r && ny >= 1 && ny <= c && a[nx][ny] == '#')
{
vis[nx][ny] = 1;
q.push({nx,ny});
maxx = max(maxx,nx),minx = min(minx,nx);
maxy = max(maxy,ny),miny = min(miny,ny);//bfs时求矩形的角
}
}
}
chang = maxx - minx + 1;
kuan = maxy - miny + 1;//矩阵大小
}
bool check(int k,int s)
{
for(int i = k;i <= k + chang - 1;i++)
for(int j = s;j <= s + kuan - 1;j++)
if(a[i][j] != '#') return 0;
return 1;
}//判断是房子还是牛群
int main()
{
scanf("%d%d",&r,&c);
for(int i = 1;i <= r;i++)
for(int j = 1;j <= c;j++)
cin >> a[i][j];
for(int i = 1;i <= r;i++)
{
for(int j = 1;j <= c;j++)
{
if(!vis[i][j] && a[i][j] == '#')
{
chang = kuan = 0;
bfs(i,j);if(!check(i,j)) cow++;
else house++;
}
}
}
printf("%d\n%d",house,cow);
return 0;
}
T2 小魔女帕琪
乏了,被这道题爆杀
做期望做多了以为是
想起了被列队春游支配的恐惧c
记
注意到期望中权值乘的概率是一样的:
所以只需要求出权值和
一个施法区间权值为1,那就是求总方案数
首先,一个施法区间排列数为
其次,考虑分布位置,这点和列队春游如出一辙,可以得到共
再让剩下位置自由排列造不同方案,共
还有一点:区间内第
那么
化简可得
md
T3 魔法阵
拿条件卡枚举范围的题
设一个魔法阵的值分别为
最暴力是
for(int i = 1;i <= m;i++) scanf("%d",&a[i].val),a[i].id = i,cnt[a[i].val].push_back(i);
sort(a + 1,a + m + 1,cmp);
for(int i = 1;i <= m;i++)
{
for(int j = i + 1;j <= m;j++)
{
for(int k = j + 1;k <= m;k++)
{
int A = a[i].val;
int C = a[j].val;
int D = a[k].val;
int B = A + 2 * (D - C);
if(A < B && B < C && C < D)
{
if(cnt[B].size() == 0) continue;
if(3 * A + C > 4 * B)
{
for(int l = 0;l < cnt[B].size();l++)
{
num[a[i].id].a++;
num[a[j].id].c++;
num[a[k].id].d++;//这三条当时放到循环外面了,没有和B个数同步,就炸了
int h = cnt[B][l];
num[h].b++;
}
}
}
}
}
}
现在考虑优化
事实上,上面的优化有一步可以借鉴:使用桶存储出现次数
那么,我们就尝试把所有物品都分到桶里,到时候直接枚举权值而不是具体的哪一个
那么就要讨论枚举的权值的取值范围
我们把等式带入不等式:
设
设
最大差值达到了
枚举
显然,只要保证图中的相对关系不变即可形成魔法阵,那么,不妨令
每一对
求
这逆天思路是我不配
//这里由于枚举的都是权值,所以a/b/c/d[i]表示当权值为i时对应的a/b/c/d的值
for(int t = 1;t * 9 < n;t++)
{
int sum = 0;
for(int D = 9 * t + 1;D <= n;D++)
{
int C = D - t;
int B = C - 6 * t - 1;
int A = B - 2 * t;
sum += tong[A] * tong[B];//前缀和累加
c[C] += sum * tong[D];//计算方案数
d[D] += sum * tong[C];
}
sum = 0;
for(int A = n - 9 * t - 1;A >= 1;A--)
{
int B = A + 2 * t;
int C = B + 6 * t + 1;
int D = C + t;
sum += tong[C] * tong[D];
a[A] += tong[B] * sum;
b[B] += tong[C] * sum;
}
}
T4 [HAOI2016] 字符合并
考场上题都没看懂
又是区间合并状物,区间
囊括要素法定义
接着细化含义:
采用区间的
不妨设定
那么可以写出方程
接下来关于初始化和特殊情况:
重点来了:有些区间合并若干次后长度正好为
我们考虑一下这样的区间的长度有什么性质
每次合并相当于砍掉
那么对于这种区间,加的权值就是
接下来是超级细节环节
- 断点枚举
由于合并的区间不重叠,那么每次就要跳过已合并的部分
l -= (k - 1)
- 状态的枚举
就是
for(int l = j - 1;l >= i;l -= (k - 1))
{
for(int s = 0;s <= ?;s++)
{
dp[i][j][s << 1] = max(dp[i][j][s << 1],dp[i][l][s] + dp[l + 1][j][0]);
dp[i][j][s << 1 | 1] = max(dp[i][j][s << 1 | 1],dp[i][l][s] + dp[l + 1][j][1]);
}
}
当
int r = 余数;
if(r == 0) r = k - 1;
for(int l = j - 1;l >= i;l -= (k - 1))
{
for(int s = 0;s <= (1 << r) - 1;s++)
{
dp[i][j][s << 1] = max(dp[i][j][s << 1],dp[i][l][s] + dp[l + 1][j][0]);
dp[i][j][s << 1 | 1] = max(dp[i][j][s << 1 | 1],dp[i][l][s] + dp[l + 1][j][1]);
}
}
此时进行的是一个初始化操作(把一些极小值变成
- 求余
进入特别
选择哪一种呢?
特殊
显然是第二种求余方式
- 特殊
这里不能直接按照
复杂度大概是个
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
for(int s = 0;s <= (1 << k);s++) dp[i][j][s] = -inf;
for(int i = 1;i <= n;i++) dp[i][i][a[i]] = 0;
for(int len = 2;len <= n;len++)
{
for(int i = 1;i + len - 1 <= n;i++)
{
int j = i + len - 1;
int r = (len - 1) % (k - 1);
if(r == 0) r = k - 1;
for(int l = j - 1;l >= i;l -= (k - 1))
{
for(int s = 0;s <= (1 << r) - 1;s++)
{
dp[i][j][s << 1] = max(dp[i][j][s << 1],dp[i][l][s] + dp[l + 1][j][0]);
dp[i][j][s << 1 | 1] = max(dp[i][j][s << 1 | 1],dp[i][l][s] + dp[l + 1][j][1]);
}
}
if(r == k - 1)
{
maxx[0] = maxx[1] = -inf;
for(int s = 0;s <= (1 << k) - 1;s++)
{
maxx[c[s]] = max(maxx[c[s]],dp[i][j][s] + w[s]);
dp[i][j][0] = maxx[0];dp[i][j][1] = maxx[1];
}
}
}
}
ll ans = -inf;
for(int s = 0;s <= (1 << k) - 1;s++)
ans = max(ans,dp[1][n][s]);
2024.4.5
今天没带脑子,不知道为啥老想睡,再加上题不好搞,就gg了
后两道题完全跟概率和期望没啥关系,顶多就是做个除法,T3组合,T4数据结构(听说优化暴力也行)
P4550 收集邮票
还算正经考期望的
看形式想到Jon and Orbs,然后就被带偏到二维
其实后来想想也确实不一样,那道题次数题目说了是收敛的,所以可以范围搞大点,这题次数没说有啥性质,得维护
然后倒序(取了
设从
对于次数,有
对于价格,结合每次的价格等于是第几次,由期望原始定义
设
因为
再结合有
答案就是
for(int i = n - 1;i >= 0;i--)
{
g[i] = g[i + 1] + n * 1.0 / (n - i);
h[i] = ((2 * g[i] + 1) * i * 1.0 / n + (h[i + 1] + 2 * g[i + 1] + 1) * (n - i) * 1.0 / n) * n * 1.0 / (n - i);//要移项
}
还有一种办法是按照分手是祝愿那题分析,不同的是没有“抽错”的代价
P4397 [JLOI2014] 聪明的燕姿
我们知道
那么
暴力枚举质数会炸,那么考虑搜索
每次枚举
void dfs(int now,int id,int num)
{
if(now == 1)//不能解分解
{
ans[++res] = num;
return;
}
if(isprime(now - 1) && now > pri[id]) ans[++res] = num * (now - 1);//形如1+p的区块
for(int i = id;pri[i] * pri[i] <= now;i++)
{
//cout << 114 <<endl;
int base = pri[i];//p_j^(a_i)
int sum = pri[i] + 1;区块总和
for(;sum <= now;base *= pri[i],sum += base//更新最高次幂和综合)
{
if(now % sum == 0) dfs(now / sum,i + 1,num * base);//进入下一区块并用最高次幂更新答案
}
}
}
//记得要给ans数组排序
P4492 [HAOI2018] 苹果树
是的,因为你会发现所有树的出现概率都是
求这个东西是组合(CaO)
我们不从点入手,从边开始,考虑一条边的贡献
对于
我们可以枚举
接下来考虑子树内和子树外点的分布(编号和形态)
对于以
从中选择的方案有
对于子树外部,根节点为
所以
//用递推法求组合数
for(int i = 2;i <= n;i++)
{
for(int siz = 1;siz <= n - i + 1;siz++)
{
ans = (ll)(ans + siz * (n - siz) % p * jie[siz] % p * C[n - i][siz - 1] % p * i % p * (i - 1) % p * jie[n - siz - 1] % p) % p;
}
}
P2221 [HAOI2012] 高速公路
对于每次询问,分母就是
所以本质还是维护区间修改和区间和
暴力最高水(这是蒟蒻水平,有人能直接水过去)
那就上数据结构呗
不想写线段树
和上道题一样,考虑一条边会被走几次
后面为了方便,边
对于
拆开
维护
对于修改(举个例子):
加的是后面一部分
又臭又长的代码就不放了
2024.5.2
T1 时间复杂度
大模拟,细节题
考场上挂了
然后改到
这种题没数据就看不出问题
T2 Emiya 家今天的饭
卫宫教的
考场上看出和组合有关,没搞出式子,骗了
有些性质get到了
-
每行只能选一个框
-
每列选的框数不超过总菜数一半
发现第二个性质不好维护,列与列之间微调一下就成了新方案
但是他的反面却很简单:最多只有一列框数会超过一半
所以考虑容斥
那么可以枚举不合法列,然后总数-总不合法就行了
假设当前枚举第
结合行与行之间相对独立,可以使用
设
因为一行只能选一个,所以这一个要么在
在
不在,则除
还可以不选,累计上一行状态
所以
第
总数:每一行可选总数为
那么合法的就是
把m打成n都有60就很难评
接下来优化,肯定是压
发现后两维一定满足
那么
其他的不变
考虑到维护差值,就要平移防止下标为负
坑点:初始化也平移了,
T3 P7098 [yLOI2020] 凉凉
好名字
状压铁路修没修状态,但不知道深度和铁路状态咋搞
后来一看,全TM是预处理了
-
第
条地铁修在 深度的花费 -
是否相撞的
-
在深度
修建状态为 的花费
对于
然后就是超简单
得到一个record
这时有个巧妙方法:我们可以枚举
for(int s = S;s;s = (s - 1) & S)
dp[i][S] = min(dp[i][S],dp[i - 1][S ^ s] + w[i][s]);
这样就过了
T4 [联合省选 2020 A]树
想水链的
可以使用
用的差分(??!?!?!?!)
规定:
我们发现,深度增加(减少时),就是所有的
所以重点就是维护整体变化事的异或和,这样就能从子节点出发计算贡献
由于是位运算,考虑按位计算贡献
类比十进制或是手算可得:改变百位需要加
进一步的,第
再把
也就是说,异或某点的时候,对于第
级祖先对应的第
每次
具体的,对上面区间的左端点和右端点
但区间又有一大堆
但再看通式就发现
设
后续操作及代码参照第一篇题解
太吊了我一定写不出来
然后看看一般的
2024.5.3
发现现在思维痼疾一堆,没救了
T1 Stack of Presents
思路想到了
考虑到放回去时可以改变顺序,因此对于每次需要挪动前面礼物的位置
本来记录个
while(t--)
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
last = 0;//上一个需要挪动前面礼物的位置
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++) scanf("%d",&a[i]),pos[a[i]] = i;
for(int i = 1;i <= m;i++) scanf("%d",&b[i]);
ll ans = 0;
int cnt = 0;//已经送出的礼物数
for(int i = 1;i <= m;i++)
{
if(pos[b[i]] < last) ans++,cnt++;//在挪出位置前,可以通过改顺序使其贡献为1
else ans += 2 * (pos[b[i]] - cnt - 1) + 1,cnt++,last = pos[b[i]];
}
printf("%lld\n",ans);
}
T2 棠梨煎雪
在某oj上暴力碾了std可海星
如果有
而且如果有一列有
暴力就是枚举每一列和
优化也是优化这里并将两种操作合并
区间查询可以使用线段树+位运算,大不了就是开30棵
对于问号位,我们只关心这一列有没有已知元素,因此出现问号时,问号赋为
由于已知出现一个就确定了这一列,所以使用 |
运算
代码可参照这位巨佬的
T3 打砖块
本来按坐标
还把本来是预处理的操作曰到
考虑子弹只会竖着走,所以把每一列打包,预处理搞
定义
然后这题的坑就在
在打包列的时候,会把
-
得有子弹去打
-
从
得到的子弹不一定继续沿着这一列打,样例就是
结合第二条,就有必要在
因此,记
再记
拿样例来说,
而
那么这样一来就能模拟第二条了
相应的把
dp[i][j][0] = max(dp[i][j][0],dp[i - 1][j - l][0] + sumY[i][l]);//整合Y
if(l > 0) dp[i][j][1] = max(dp[i][j][1],dp[i - 1][j - l][0] + sumn[i][l]);//第i列收尾, 奖励给j-l那部分留着,此时前面停到了N
if(j > l) dp[i][j][1] = max(dp[i][j][1],dp[i - 1][j - l][1] + sumY[i][l]);//第i列收尾,l颗和奖励都梭哈到第i列,那么对于j-l部分来说j-l发也是全梭哈的
坑点:
1.
if(vis[i][c])
sumY[c][cnt] += a[i][c];//等价认为不消耗
else
{
++cnt;//新增一发
sumY[c][cnt] = sumY[c][cnt - 1] + a[i][c];
sumn[c][cnt] = sumY[c][cnt - 1] + a[i][c];
}
2.答案不比大小,就是
T4 斗地主
考试的时候先水了
根据经验来说,肯定先挑一次出牌多的方法出(当然也不一定,所以搜索而非纯贪心)
因此先看顺子,再看带牌
还有一点就是,对牌,三牌,炸弹什么的其实不用特意判断看,因为每种牌最多不超过四张,出法还都是单一数值(没有组合),所以只需最后遍历一遍,还有剩的话再出一次就行了
2024.5.19
概率期望组合乱杀赛
T1&T4 P5516 [MtOI2019] 小铃的烦恼
矩阵没有意义,因为每个都是
有点像分手是祝愿,但又很不一样
设
一个操作分两大类三种情况:
- 把一个颜色改成
,花了1步,再花 步即可 - 把
改成别的,同上,共花了 步 - 与
无关
设修改涉及到
对于第一大类,差别只在于
对于第二大类,没有修改
考虑到所有的都是操作了一步,所以有式子
化简得到
然后根据经验可知这东西退化成
就完了?
没有
事实上,还存在着改着改着
设
还是上面的两大类,再把修改
化简得到
这是个等差数列,再结合定义得到
可以算出
那么根据条件概率,所有的步数都要乘上对应的可行概率,所以得到
再代入概率表达式
这样的话,再根据退化成线性,就可以跑一个高斯消元板子的简化版求出
但是有点麻烦,还可以设
那就会有
即
相当于每次的差就是给上一次的差加一个数,特殊的是,最初的差(没加数)就是
从
令
得到
然后就递推了
T3 [JXOI2018] 游戏
组合
第一次读错题了,后来才发现是怎么回事
就是
虽然提醒
这也就是说:时间就是从头开始,一直到最后一个未被提醒的位置
显然,未被提醒就是前面没有自己的因子,我们可以把这类数预处理出来,假设有
那么问题就变成了一个期望问题:最后一个数的位置(相对于开头)是权值,与落到该位置的概率相乘并求和,再给最后的答案乘以
对于一个特殊数,它可能的最后位置应该在
那么同时,剩下的
总的分布是
所以
尝试化简:
所以
最后一个西格玛很熟悉了,就用公式
所以
ps:代码实现时用埃筛思路即可,但为了速度要把标记数组开成
T2 SSY的队列
状压:
接下来通过优化状态,我们可以得到如下性质:
-
我们可以按照膜
的余数将所有数字分类,则不同类的数字相邻一定合法,考虑到同一类的数等价, 那么答案就是对类的排列方案数乘上每个类所含数个数的阶乘,设 表示当前最后一个数为第 类,各类数用掉了 个 -
顺序无关,即对于 若 ,则 ,这个性质可以用来使得 ,减少了状态数
2024.6.2
T4 围栏障碍训练场
写
状态都想好了,
然后每次更新都要用上一次的左端点和右端点来更新
这个“上一次”就是预处理的内容(不好处理也可以类似的处理“下一次”)
然后没写,硬存每次新的左右端点,拉了一大坨才
考虑到预处理是
啊,
再优化的话,可以使用区间覆盖+单点查询,即每个区间把自身范围内的高覆盖成自己所在高度,然后对于端点,每次查询一下就可以了
要写线段树,like this
T1 等差子序列
想着是用差分数组乱搞暴力,但是还有更简单的
枚举差值得数值,再判断一下位置单调性和数值单调性是否同步就可以
注意的是单调性有增有减
T2 [Jsoi2015]非诚勿扰
我们先计算一下对于第
以第一个人为例,他被选中的概率 = 直接被选中的概率
设列表长度为
然后
类似的,就能得到第
对于一对女人
观察要求形式,可以使用树状数组实现类似求逆序对的方法维护
T3 [CSP-S 2021] 括号序列
考虑使用题目给的规则扩展,所以是区间
定义
-
:形如(...)
的字符串 -
:形如***..**
的字符串 -
:形如(...)**..*(...)
的字符串,即左右均为括号 -
:形如(...)*...*(...)**
的字符串,即左边为括号,右边为*
-
:形如*..*(..)
的字符串,即左边为*
,右边为 -
:形如*..*(..)***
的字符串,即左右均为*
接下来搞
-
,前提是 能配成一对括号- 含义是括号内除了两边是星星的情况不合法其他均可
-
特判即可 -
- 含义是第3,4种满足左边为括号开头,然后右边补一个括号,是情况1,还有一种特殊的是整个是一个括号序列
-
- 含义是在第3种情况后面加一串星星构成第四种情况
-
- 含义是开头为星星的为第5,6种情况,在结尾补一个星星成为情况5
-
- 含义是在开头为星,结尾为括号的情况后面补一串星得到第六种情况,特别的,全是星属于该情况
答案为
2024.6.9
T1 小奇挖矿2
还没悟透,还得悟
这道题有一个很○○的性质:如果两个行星之间的距离
得到这个性质可以打表,也可以用一些奇奇怪怪的数学定理(因为此时距离可以写成
这样,我们就可以离散化,把位置从星球编号变成距离,如果两星球间距离大于十八,就只给距离加18就行了
注:
-
方便起见可用倒序
-
别老惦记着map,会被卡到
T2 小奇的矩阵(matrix)
式子都有了
设
接下来就要维护平方的和以及和的平方
然后就到了本蒟蒻必翻车环节:可以把和扔进状态枚举,反正最大才
所以定义
那么当
初始化:
后面两道都和树有关,难绷
T3 小奇的仓库(warehouse)
先不管异或,用树剖求有
稍微解释一下solve:对于父子节点
然后看看异或
观察到
T4 Kamp
树型dp里的题但我跳了
树形
对于此题,我们可以定义
那么对于边
类似可得
特别的,根没有
那么,对于以点
我们回到
那
但实际上,送完最后一个人后没必要回来,所以有两种选择
-
先
外再 内,停在 内不回来 -
先
内再 外,停在外面不回来
每种方案都是在原来和的基础上减掉一个回来的花费
那么减掉的肯定越大越好,所以就是求以
最后有一个细节:计算
2024.7.6
T1 公式求值
根据样例写出来发现每一位都是个前缀和,搞一搞就行了
T2 最长的Y
std:
考虑初中学过的
然后二分区间按上面类似的方法搞就完了
有一点就是
设第
可以通过维护前缀和等方法得到距离
T3 交换序列
把???
接下来考虑另一种方法:不妨枚举加入的字母来合成答案串,再判断一下符不符合条件就行了
合成的串可以使用类似状压的方法压缩表示,有
想到这里就基本快到正解了
这里有一个隐藏限制:
理由很简单,超过这一个之后就可以对序列全排列了,多出来的交换次数没什么用
那么就可以把它扔进
定义
预处理原串中三个字母的数量、位置等,通过枚举新加入的字母来转移答案
T4 最长路径
由于数据规模小,所以不妨直接枚举第
定义
但是样例二说明有些相等的情况必须取
那就不妨都先取上,然后再跑一边不取的情况看看能不能更优就行了
五次方的复杂度能过就是了
2024.7.8
5道题的一天
T1 分糖果
按余数分,有
那么就有两大种方案
-
尽可能多的凑
,剩下的凑 -
尽可能多的凑
,剩下的凑
两种分别算求
把j+2写成j + 没删调试痛失100
T2 乒乓球
整洁:
考虑周期 (废话)
大周期就是两个人的比分再次相同的中间经过的部分
但是要注意的是:不是一开始就进入周期
这里用到一个技巧:当
那么比分最多$12 \times 12
找到周期后,由于每个周期满足两人赢的局数增量一样,且比分不变,所以可以跳过不少
再把首位未进入周期的部分暴力掉就行了
T3 与或
开的ll但scanf %d 痛失暴力分30
这里有一个策略:把|放&后边,值一定不变小
好像也很好理解,
所以直接把所有|放到最后得到最大值
然后枚举前面,看能不能在不改变值的情况下放|,形成|||..&&&..|..|的样子
这里,为了快速得到||||或者&&&&&的答案,要预处理一个位前缀和,再根据特性搞
T4 跳舞
有暴力,但感觉不好打,有用到状压
看数据范围像是区间
设
然后定义
这里要
T5 音乐播放器
很熟悉了
肯定要做
如果第
接下来根据样例,我们需要计算如果前面出现了
这里可以借鉴非诚勿扰的等比数列方法:
此处介绍另一种方法:
..
2024.7.9
T1 超市抢购
只要当前不够就从后一个搬,哪怕后一个也不够,反正后面会补齐的
ll ans = 0;
for(int i = 1;i <= n;i++)
{
if(delt[i] < 0)
{
ans += abs(delt[i]);
delt[i + 1] -= abs(delt[i]);
}
}
cout << ans;
T2 核酸检测
dp
定义
转移就是
接下来考虑
根据
那么就要保证开头在这个区间内的区间的右端点都大于
可以预处理一个
由于要覆盖最后一个区间,所以在
接下来考虑求方案
如果
T4 龙珠游戏
屌题
数据明示区间
明确的一点是,如果神龙拿走的珠子不在端上,其实可以选择“不取”,反正小明取不到
那么怎么弥补没取的次数呢?
我们可以让神龙在某个时刻从左(右)端连续取走若干个珠子
这样就把两人的操作都限制在端点处,可以搞一搞
定义
那么分类
小明:必须取左/右端,此时龙决策完了
龙:
-
取左/右端,消耗一定次数
-
放弃,继承小明的状态
就有方程
答案:
T3 七龙珠
竟然用背包我服了呀(想到了不敢写)
后面写对的话瓶颈就是背包的
突破以后再说,要用科技,不用上限
那么背包出来可以拼出的合法龙珠后从大到小排序(系数为负的话从小到大,注意记得算上能量值为
有两种写法
最大的肯定是每组最大值,记为
重点是去重,想办法搞搞,比如
- dfs
目前有个剪枝就是排名
能拿
2024.7.10
能过
能过
能过
T1 算术求值
模拟出
......
T2 括号序列
先把能配上的扔掉,然后相邻两个一组按下列方式计算代价
-
或 :代价为 -
:代价为
完了
T3 Count Multiset
dp
可以做背包搞,有
ll dp[N][N][N];//dp[i][j][k]: 考虑前i个数,有j个不为0,和为k
int n,m;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin >> n >> m;
dp[0][0][0] = 1;
for(int i = 1;i <= n;i++)
for(int j = 0;j <= n;j++)
for(int k = j;k <= min(i * j,n);k++)
for(int o = 0;o <= min(m,j) && o * i <= k;o++) //枚举重复元素数量,重复的就是i
dp[i][j][k] += dp[i - 1][j - o][k - o * i];
for(int i = 1;i <= n;i++) cout << dp[n][i][n] << endl;
return 0;
}
补上取余能到70
std:
继续沿用补
-
补一个
,从 转移 -
整体加
,从 转移
这里巧妙的是通过补
那么考虑限制条件,很显然第一种操作最多补
所以补充一个
西格玛可用前缀和优化
T4 选数
dfs
分别把质因子给
为了防止重复有一些特判
剪枝:考虑到要保证个数最优,肯定大质数的幂次都是
T5 划分序列
之前有一道类似的
二分最大值,那么两种情况的最大值都不超过二分的值
接下来定义
考虑到有两种代价计算方式,如果分别
这里巧妙的是,把一种方式作为转移条件
于是,把区间和作为限制
定义
那么
前面的单调队列压线性
2024.7.11
上强度了
T1 最短路
数据范围明示
考虑到瞎 搞
将数组排序,就可以保证
还有一点就是点权不能混进
T2 方格取数
为什么有人(Byj大神)四重for有90啊卧槽
各种卡时如枚举长宽,其范围从[1,n]调到[2,n-1],定义kk为2倍并使用位运算,各种特判+return 0可以获得最少40,最多75
还有clock的,魔法常数的...
教练给的是二分套二分枚举长宽,应该是
但其实这也是个半玄学,因为对于同一大小的矩形,不同位置的非法情况也不一定相同,和要么小于
还有单调栈+悬线法做的
悬线法就是在某些格子不能选的情况下,处理出每一列的“悬线”,就是最高/低能到哪里,因此每根线都有一个初始位置和高度,接下来就用这根线左右扩展,看能扫出多大的矩形,由于肯定以低的线为界,要用单调栈维护
那么这道题就是先找到一个和
是
T3 数组
大数据结构题
根据计算公式
可以去分别维护前面的区间乘积和后面的质因数种类
看到
然后就是线段树
T4 树
核心就是一个
在树上跳
预处理
那么就设置一个阈值为
否则暴跳就完了,反正不超过
要注意的是
2024.7.13
T1 炒币
贪心,找单调区间,峰点买入低谷卖出,都是在区间交界处
dp也能搞,但太菜了
T2 凑数
先按性价比(
假设优先级是
注意可以选
O(n)dp应有30但RE了就。。。
T3 同构
猜结论或打表
比较好猜的是
- 所含质因子种类相同的数字可以互相换
也好解释,互不互质就看的是质因子
但下面这个就不好猜了
- 所有满足
相同的质数 可以互相换
粗略的理解就是有些质数可以和
那么如果能交换,很明显链的大小要一样,那么就是
还有一个就是
和 的数可以互换
用上面建图方式理解(不互质的连一条边),那么这些数肯定都是单点,互换肯定可以
T4 最近公共祖先
考虑黑点的贡献
一个黑点肯定会对自己的子树提供一个备选
再根据询问可知:黑点的祖先 会为 其子树中不含黑点部分 的点 提供一个备选(以该黑点为跳板即可)
那么每更新一个黑点,就可以不断跳父亲来更新对应部分的点的权值
可以
但是树的深度未知,跳父亲直到根节点就可能会很慢
事实上,每个点作为父亲产生贡献只需要一次,因为权值是固定的,所以跳到一个已经用过的父亲,就可以停了
但是注意的是,应当先更新完再停,因为即使父亲相同,黑点不同,要更新的子树也不同
2024.7.14
爆的最爽的一集
T1 道路
假设原图中两点距离为
-
为偶数: -
为奇数:
可得当距离为奇数时,会多一个1
那么就统计有多少对点的距离为奇数,然后加到原图中任两点距离上,最后除以二即可
那么怎么求距离为奇数点对数呢?
观察
减数是个偶数,那么加数一奇一偶即可
就是深度为奇数点
完了
附:原图中任两点距离计算(考虑边的贡献):
T2 集合
最大化异或和的经典做法是
对于条件
对于条件
又因为不知道
这里注意进入一个节点的条件:
-
经过该点的最小值
-
该点有编号(存在)
走到中间不行了立刻输出-1
T3 科目五
没开
二分应该是伪的,但考场上(下)有人能过
问了一下发现
后来发现二分能过,但是要随机化加一个
正解
定义
那么方程就是
朴素版本是
接下来用一些奇技淫巧可以发现每次的转移点
那就可以仿照单调队列维护转移点即可 这叫单调指针罢
T4 监狱
屌题
如果有两个人的路径重叠了,那么这两个人的移动就要有先后顺序
具体的:
-
的起点在 的路径上,那么 应当先于 走 -
的终点在 的路径上,那么 应当先于 走
不妨把
然而重叠部分可能不止一个点,那么某点要连边,就要向路径上所有点连边
如果暴力连接,是平方级别的,但是有分
正解是线段树优化建图... 炸弹还是要炸死人了
2024.7.15
T1 传送带
对于当前所在点
设
那么一共有四种情况:
- 开始向左(右),从左(右)出
这里以开始向左,从左端出为例:
其他情况照着推即可,比如右始左出时
接下来考虑
根据样例可得:如果序列中有
T2 math
点击这个再点击目录里的裴蜀定理发现推广2和题目中的形式几乎一样,没取膜而已
那么根据取模尿性,可以猜测
但试验后不对
根据取余原始含义可以写出:
其中
然后变一下
再用一遍裴蜀定理可得到
再根据推广:
所以
那么答案就是
T3 biology
设
肯定爆
然后考虑到
那么对于
那么我们可以对每一条
对于一条线段,结合原始
开四个变量维护即可
注意的是,在更新
ll p = h[1] - c[i].x - c[i].y;
ll q = h[2] - c[i].x + c[i].y;
ll r = h[3] + c[i].x - c[i].y;
ll s = h[4] + c[i].x + c[i].y;
T4 english
不会
2024.7.24
从此开始难度随机排列
T4 Black and Black
构造
先令
此时已经满足了单调性,为了维护这个东西,最稳妥的方法就是在
如果
T3 White and Black
如果一个节点是黑的,那么就要统计儿子中是白色的个数,因为这些儿子要变回白色还各需一次操作
那么转化一下就可以变成:儿子数 - 儿子中黑色的个数,因为黑色的儿子肯定在给出的集合中,方便操作
再变一下就是:如果该黑点的父亲是黑色,就省了一次操作
T2 White and White
最裸的就是定义
然后考虑优化
根据取模性质可以得到:
对于
所以
又因为
所以对于每个
为了压缩空间
T1 Black and White
四个正解一个不会
非魔法能看懂的正解:
维护括号序列,原理等见这里
2024.7.25
T1 Permutations & Primes
yy构造题
首先,要防止
再把较小的质数往两边放,这样能让不含小质数的区间尽量多
再把剩下的合数扔进去,无所谓顺序
T2 树上游戏
正解二分不难想到,重点是
首先一般情况下
考试的时候口胡了从根走的
正解是每次从最深的点开始,走
不太会删点,所以换个方法,本质类似:
设
那么放点就相当于
最后要特判根节点
T3 Ball Collector
软肋题(并查集)
考虑给每个路径上点的
画一下图就发现除了树只能拿出
又因为
所以可以使用并查集维护连通块,又因为终点一堆,所以要在回溯时删掉离开的点,就是可撤销并查集
T4 P7028 [NWRRC2017] Joker
屌题
定义
然后就有了两种做法(但本质类似)
-
1.
如果
我超,斜率+凸包
那么答案查询就是二分斜率找点
接下来考虑修改
发现当修改位置为
所以对序列分块,每个块建一个包,修改点所在包重构,其余平移即可维护
-
2.
进一步化简
没修改的时候就是要最大化后面那坨,然后发现这东西可以使用第三维是
然后还是凸包,还是分块维护
...
2024.7.26
T1 Not Sitting
Tina的选择明显是在四个角,所以计算一下每个格子到四个角的最大距离,排序即可
考场上写的宽搜,就是Rahul肯定是在中间落座,然后逐层向外,典型宽搜
T3 初生的东曦,彼阳的晚意
就black and white那天的题加了个区间查询,这次用了线段树维护直径,因为直径有个性质:合并后直径的端点只可能来自合并前两棵树各自直径的四个端点,证明略,而且这个还比括号序列好理解,好理解就相对好写
但标题是为什么呐
嗯...
T2 s
可以组合,可以dp
教练给的dp法没看懂(doge)
组合法原理见这里
T4 Everything on It
科技,跳了
大概解释一下:
反演那部分不说了,套的板子,原理还行
重点看
首先明确
- 钦定部分
这部分可以分为两类:出现一次,没出现
没出现不管,对于出现一次的,设有
但是那
再枚举一下
三者相乘得到第一部分
- 未钦定部分
这部分简单,加不加
然后两部分以及开头的组合乘起来,就是
2024.7.27
萎了
T3 组合
看到
就是每时每刻向右走次数不少于向左走次数,裸的
把这对到情景0去了...
此时横纵方向都是
这部分没限制,和卡特兰没关系,同样枚举横向走了几步,然后从横、纵步数中各选出一半向某方向走,剩下的就往相反方向走
这里比较复杂
设
答案
T2 树
高消有
接下来看std:
定义
根据题目可以得到一个式子:
原理就是从
然后来看看怎么用它
对于所有非根结点都有上述式子,所以可以得到
先变一下形式:
然后把
解释:左边,由于加减不同,所以每个
然后就能求出
T4 大dp
原理见这里
T1 boss
设
能拿
看到
这里的阻碍就在于
用矩阵在图上的应用来理解是没有问题的,相当于向
借助此将
2024.7.29
T1 花间叔祖
余数种类最小肯定同余的多,再结合同余条件是差为模数倍数,很容易想到对差分数组求一遍
没想到
T2 合并r
想到
定义
考场上只想到添数法转移,然后就不会搞添
后来发现还可以通过给和为
初始化:
T3 回收波特
神秘题
一点思路没有,还好暴力给的多点
首先有这么一个结论:
如果两个波特的坐标某时刻是相反数,那么在以后的操作中他们的坐标一直是相反数
证明不难,yy一下就出来了
这就说明,对于对称的部分,我们只维护一半就行了,另一半可以推出来,实现的话可以连边
由于波特太多,有个小
查询的时候,就相当于跑链直到回至原点/没映射的点,这里可以使用记忆化优化
T4 斗篷
读都读不进去...
(字符串
(换成
... (玩集贸跳了)
还有一点没说就是
std:
定义
对每个点都可以有下图的关系:
贡献统计可以使用单点加减
代码里应该是把这个图逆时针旋转
还有一点,就是跑一边只能求出正的三角,还需把图反转再跑一遍
2024.7.30
昨天晚上没关空调睡觉结果感冒了,还一直咳嗽,今天只想睡觉...
T1 XOR Matching 2
枚举
T2 S
像这个
不同点就是现在求最小次数
同样对最终串
T3 Y
屌题
解析戳这里
T4 O
是这个
难蚌题,目前知道原理但不会写
2024.7.31
咳嗽,呕了一上午
T1 黑客
直接枚举
T2 密码技术
首先发现行列互不影响,那就是行列方案数乘积
如果
T3 修水管
状压暴力应有45但没改出来
考虑一个大炮
一个水管有漏水可能的前提是前面的水管不是被修了,就是没事儿,考虑从这点出发求解概率
定义
对于第
所以有
接着设
最后
T4 货物搬运
之前分块有一道入门是单点插入,好像没做
没啥思维,vector + 分块即可
还有平衡树写法,不会
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!