20230918 总结

总结

creatTime:20230918

学习重点:字符串、生成树

今日测试

am

pm

上午成绩

题目 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

    如何卡常:

    1. 自己写 max 函数;
    2. 使用快读、快写;
    3. 使用离散化,离散化后若 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 初步完成

posted @ 2024-03-23 20:27  CheemsaDoge  阅读(6)  评论(0编辑  收藏  举报