20230918 总结
总结
creatTime:20230918
学习重点:字符串、生成树
今日测试
上午成绩
题目 | snow | tickets | word |
---|---|---|---|
实际得分 | 90 | 90 | 100 |
期望得分 | 100 | 100 | 100 |
Delta值$\Delta$ | -10 | -10 | 0 |
错误原因 | 评测姬太拉,莫名Tle,卡常后通过 | 算法错误,具体问题未知 | AC自动机 |
(目前)已掌握并改错 | 1 | 0 | 1 |
$Snow$
不同的雪花往往有不同的形状。一共有 n 个时刻,给出每个时刻下落雪花的形状,用不同的整数表示不同的形状。同学们不希望有重复的雪花。你可以从任意 a 时刻开始,在 b 时刻停止。a 到 b 时刻中间的雪花也都将被收集。他们希望收集的雪花最多。
-
双指针 ( $O(n)$ )
RT,没什么讲的,就是双指针,不过由于评测姬太拉,需要卡常
r
指针右若未被收集,则r++
,并收集此时r
的血花。- 若已被收集,则记录目前
ans=max(ans,r-l+1)
,并抛弃此时l
的血花后l++
直到
r>=n
输出ans
并返回0
。如何卡常:
- 自己写
max
函数; - 使用快读、快写;
- 使用离散化,离散化后若
tot
为初始值,则直接输出1
并返回0
。
实际用处其实不大,真的害怕被卡建议换个评测姬。
$Tickets$
有一个数列 ${a_n}$ , $a_0=1,a_i+1=(A\times a_i+a_i\ mod\ B)\ mod C$,要求这个数列第一次出现重复的项的标号。
$\large\text{\color{red}要求仅凭心算得到结果!!}$
-
递推并使用
map
统计创建
map<int,bool>fkccf
递推从a_0
开始计算,当计算到a_i
时- 若
fkccf[a_i]==0
,则赋值fkccf[a_i]=1
。 - 若
fkccf[a_i]==1
,则输出i
,并返回0
。
- 若
$Word$
一篇论文是由许多单词组成。一个单词会在论文中出现多次,现在请问每个单词分别在论文中出现多少次。
-
AC自动机
跑一遍
AC自动机
,每一个节点保存一下属于多少字符串,为它的权值。然后一个节点表示的字符串在整个字典中出现的次数相当于其在Fail树中的子树的权值的和。/*code by CheemsaDoge*/ //表白CCF #include <bits/stdc++.h> using namespace std; const int MAXM=1e6+1145;const int MAXN=1e6+11145;const int INF=2147483647ll;//2^31-1 #define ll long long #define pc putchar('\n') #define pk putchar(' ') /*---------------------------------pre------------------------------------*/ [[maybe_unused]]inline void _File() { freopen("word.in","r",stdin); freopen("word.out","w",stdout); } int n,cnt,last; int a[MAXN],h[MAXN],fail[MAXN]; int tr[MAXN][28],sz[MAXN]; char s[MAXN]; inline void insert(char *_s,int x){ int u=0,len=strlen(_s+1); for(int i=1;i<=len;i++){ if(!tr[u][_s[i]-'a']) tr[u][_s[i]-'a']=++cnt; u=tr[u][_s[i]-'a']; sz[u]++; } a[x]=u; } inline void build(){ int i,head=0,tail=0; for(i=0;i<26;i++) if(tr[0][i]) h[++tail]=tr[0][i]; while(head<tail){ int x=h[++head]; for(i=0;i<26;i++) if(tr[x][i]) h[++tail]=tr[x][i],fail[tr[x][i]]=tr[fail[x]][i]; else tr[x][i]=tr[fail[x]][i]; } } int main(){ // _File(); read(n); for(int i=1;i<=n;i++) scanf("%s",s+1),insert(s,i);build(); for(int i=cnt;i>=0;i--) sz[fail[h[i]]]+=sz[h[i]]; for(int i=1;i<=n;i++) write(sz[a[i]]),pc; return 0; } //码丑得自己都受不了了。。
下午成绩
题目 | muilktrans | party | tree |
---|---|---|---|
实际得分 | 0 | 100 | 0 |
期望得分 | 70 | 30 | 0 |
Delta值$\Delta$ | -70 | 70 | 0 |
错误原因 | LOJ 上评测 AC , 未来算算 RE |
N/A | |
(目前)已掌握并改错 | 1 | 1 | 0 |
$Milktrans$
求次小生成树。
-
很明显的次小生成树问题。
次小生成树求解方法:1、求最小生成树,统计每条边是树边还是非树边,同时把最小生成树建出来。
2、预处理任意两点间的边权最大值dis[a][b]
。$O(n^2)$ 用dfs
或者bfs
3、依次枚举所有非树边,求min(sum+w-dis[a][b])
,满足w>dis[a][b]
,因为这个题是要严格次小生成树。定义函数
void dfs(int u,int fu,int maxd1,int maxd2,int *d1,int *d2)
起点u
->终点
,father
判断是否回到原起点maxd1
最大值,maxd2
次大值
再用d1[u]
保存从now
->u
的最大路径权值, 用maxd2[u]
保存从now
->u
的次大路径权值
循环遍历与重点u
相连接的每一个点v
,如果没有回到father
就继续。/*code by CheemsaDoge*/ #include <bits/stdc++.h> using namespace std; const int MAXM=1e6+1145;const int MAXN=1510;const int INF=2147483647ll;//2^31-1 #define ll long long #define pc putchar('\n') #define pk putchar(' ') /*---------------------------------pre------------------------------------*/ int n,m; int fa[MAXN]; int dist1[510][510],dist2[510][510]; struct node{ int a,b,w;bool flag; bool operator<(const node & x)const {return w<x.w;} }edge[MAXM]; int head[MAXN],totr; struct Edge{ int to,w,nxt; }e[MAXM*2]; void ini() {for(int i=0;i<=n;i++) fa[i]=i;} inline int find(int x) {return fa[x]!=x?fa[x]=find(fa[x]):fa[x];} void add_edge(int a,int b,int c) { e[++totr].to=b; e[totr].w=c; e[totr].nxt=head[a]; head[a]=totr; } void dfs(int u,int fa,int maxd1,int maxd2,int ti) { dist1[ti][u]=maxd1;dist2[ti][u]=maxd2; for (int i=head[u];i;i=e[i].nxt) { int To=e[i].to,W=e[i].w; if(To!=fa) { int td1=maxd1,td2=maxd2; if(W>td1) td2=td1,td1=W; else if(W>td2&&W<td1) td2=W; dfs(To,u,td1,td2,ti); } } } int main() { read(n);read(m); for(int i=1;i<=m;i++) { int a,b,w; read(edge[i].a);read(edge[i].b);read(edge[i].w); edge[i].flag=0; } sort(edge+1,edge+1+m); ini(); ll sum=0; for(int i=1;i<=m;i++) { //Kruskal int a=edge[i].a,b=edge[i].b,w=edge[i].w; if(find(a)!=find(b)) { fa[find(a)]=find(b); sum+=w; add_edge(a,b,w);add_edge(b,a,w); edge[i].flag=1; } } for(int i=1;i<=n;i++) dfs(i,-1,0,0,i);//get max/sec_max dis from i to v ll res=5e18; for(int i=1;i<=m;i++) if(edge[i].flag==0) { int a=edge[i].a,b=edge[i].b,w=edge[i].w; ll t; if(w>dist1[a][b]) t=sum+w-dist1[a][b]; else if(w>dist2[a][b]) t=sum+w-dist2[a][b]; res=min(res,t); } write(res); return 0; }
$Party$
有向图,求从 $i\in [1,n]$ 出发构成的经过节点
x
的环的最小值的最大值。
-
两次
Dijkstra
宇宙究极无敌大水题,两次
Dijkstra
,分别每条边正着建和每条边反着建后跑Dijkstra
,找到使dist1[i]+dist2[i]
最大的值并输出。真的水????
$Tree$
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有
need
条白色边的生成树。
-
最小生成树+二分
其实题目给的很明确
让你求一棵最小权的恰好有need
条白色边的生成树。
这不是明摆着要求最小生成树吗?刚要开始写,问题接踵而至,这个白边的数量怎么控制呢,如果多了或者少了,如何增减?
进入正题: 总所周知,在跑kruskal的时候,我们有这样一句代码:
sort(e+1,e+1+m,cmp);
这是对边进行排序,由此可知,边权越小越靠前
我们可以通过改变白边的权值来改变它出现的位置例如
need=3
,但是此时我的最小生成树上有 $4$ 条白边,那么我可以全部加上一个数,然后某条白边突然比最小的黑边大了,我再跑kruskal
,更新之后的最小生成树就会把一条大的白边扔到后边去,替换成一条黑边,那么此时白边条数就少了 $1$ ,满足答案,统计答案时把加的这个数减掉就行了现在我们来说说怎么确定要加的这个数
我们采取二分答案的办法,因为题目中说道边权在 $[1,100]$ 所以定义l=-111
,r=111
,然后二分往白边上加权值,最后一定能卡出来一个合适的解那么问题又来了,如果说当前白边加上
mid
后,白边条数temp>need
了,然鹅,如果加上mid+1
后,temp<need
了,这可咋整题目中说到了:保证有解,所以出现上述情况时一定有黑边==白边的边权
so,我们只需要把一条黑边换成白边就好啦
在白边条数temp>need
时更新答案
最终答案ans=sum-mid*need
思路来自这篇题解
进度:
Theme: Vue
Last Edit Time: 2023.09.18 20:23
Editor: CheemsaDoge
2023.09.18 20:23 初步完成