AcWing进阶算法课Level-4 第七章 基础算法
Posted 小哈里
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AcWing进阶算法课Level-4 第七章 基础算法相关的知识,希望对你有一定的参考价值。
AcWing进阶算法课Level-4 第七章 基础算法
启发式合并
AcWing 2154. 梦幻布丁73人打卡
AcWing 3189. Lomsat gelral54人打卡
manacher算法
AcWing 3188. manacher算法92人打卡
最小表示法
AcWing 158. 项链58人打卡
构造
AcWing 516. 神奇的幻方40人打卡
AcWing 2268. 时态同步39人打卡
打表
AcWing 1412. 邮政货车20人打卡
代码
AcWing 2154. 梦幻布丁
//题意:长为n的序列,m次操作,操作1 x y将所有颜色x改为y,操作2询问当前有多少段颜色
//思路:每次暴力合并x到y需要枚举,复杂度O(n),考虑把短的合并到大的上,即可以合并y到x,反正结果是一样的,用f[x]表示颜色为x时实际上需要寻找的链的颜色,此时复杂度为logn。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e6+10;
int c[maxn], f[maxn], ans;
int hd[maxn], nxt[maxn], st[maxn], sz[maxn];
void insert(int i, int x){
if(!hd[i])st[i]=x;
sz[i]++; nxt[x]=hd[i]; hd[i]=x;
}
void merge(int x, int y){
for(int i=hd[x]; i; i=nxt[i])
ans -= (c[i-1]==y)+(c[i+1]==y);
for(int i=hd[x]; i; i=nxt[i])c[i]=y;
nxt[st[x]]=hd[y], hd[y]=hd[x], sz[y]+=sz[x];
hd[x]=st[x]=sz[x]=0;
}
int main(){
ios::sync_with_stdio(false);
int n, m; cin>>n>>m;
for(int i = 1; i <= n; i++){
cin>>c[i]; f[c[i]]=c[i];
ans += c[i]!=c[i-1];
insert(c[i], i);
}
for(int i = 1; i <= m; i++){
int op; cin>>op;
if(op==2)cout<<ans<<"\\n";
else{
int x, y; cin>>x>>y;
if(x==y)continue;
if(sz[f[x]]>sz[f[y]])swap(f[x],f[y]);
if(!sz[f[x]])continue;
merge(f[x],f[y]);
}
}
return 0;
}
AcWing 3189. Lomsat gelral
//题意:给出一棵 n 个结点的树,每个结点都有一种颜色编号,求该树中每棵子树里的出现次数最多的颜色的编号和。
//思路:dsu on tree,对树做轻重链剖分,先跑轻儿子,最后一遍跑重儿子的时候不用清空数组,复杂度降低为O(nlogn)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+10;
LL c[maxn], ans[maxn];
vector<int>G[maxn];
LL siz[maxn], son[maxn];
void dsu(int x, int fa){ //轻重链剖分
siz[x] = 1;
for(int to : G[x]){
if(to==fa)continue;
dsu(to,x);
siz[x] += siz[to];
if(siz[to]>siz[son[x]])son[x]=to;
}
}
LL cnt[maxn], tmp, tmpv, Son;
void add(int x, int fa, int val){//统计x节点的贡献
cnt[c[x]] += val;
if(cnt[c[x]]>tmpv)tmpv=cnt[c[x]],tmp=c[x];
else if(cnt[c[x]]==tmpv)tmp +=c[x];
for(int to : G[x]){
if(to==fa)continue;
if(to==Son)continue;//统计x节点的轻儿子时不统计重儿子
add(to,x,val);
}
}
void dfs(int x, int fa, int op){//遍历每个节点
for(int to : G[x]){
if(to==fa)continue;
if(to!=son[x])dfs(to,x,0);//暴力统计轻边的贡献,op=0表示递归完成后消除对该点的影响
}
if(son[x])dfs(son[x],x,1), Son=son[x];//统计重儿子的贡献,不消除影响
add(x,fa,1); Son=0; //暴力统计所有轻儿子的贡献
ans[x] = tmp;
if(!op)add(x,fa,-1),tmp=0,tmpv=0;//消除对该点的影响
}
int main(){
ios::sync_with_stdio(false);
int n; cin>>n;
for(int i = 1; i <= n; i++)cin>>c[i];
for(int i = 1; i <= n-1; i++){
int x, y; cin>>x>>y;
G[x].push_back(y);
G[y].push_back(x);
}
dsu(1,0);
dfs(1,0,0);
for(int i = 1; i <= n; i++)
cout<<ans[i]<<" ";
return 0;
}
AcWing 3188. manacher算法
//题意:给出一个长为n的字符串,求它的最长回文子串长度
//思路:朴素做法为每次选定一个中心,向左右枚举判断回文串,复杂度O(n^2)。
//马拉车:在一个大的回文串内,[mid,r]与[l,r]是对称全等的,此时对于i属于[mid,r]且回文边界不超过大的回文串时,他的回文边界等价于p[2*mid-i],然后再对超过的部分重新暴力匹配,更新最靠右的大回文串。
//string会超时
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e7+1e6+10;
char s[maxn], t[maxn<<1];
int p[maxn<<1];//p[i]记录以字符s[i]为中心的最长回文子串向两边的扩展的长度
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
scanf("%s", s); int slen=strlen(s); t[0]='~'; t[1]='#';//~防止下标溢出
for(int i=0,j=1; i<slen; i++)t[++j]=s[i],t[++j]='#';//把可能的奇数偶数长度化为奇数求解,对称中心一定是#
int ans = 0, mid=0, r=0, tlen=strlen(t); //最大回文子串的中心和边界
for(int i=1; i<tlen; i++){
if(i<=r)p[i]=min(p[mid*2-i], r-i+1);//没有超过MX回文串边界时,可用对称性求解
while(t[i-p[i]]==t[i+p[i]])p[i]++;//超过边界时,暴力匹配
if(p[i]+i>r)r=p[i]+i-1, mid=i;//更新mid和r,保证r是最靠右的
if(p[i]>ans)ans=p[i];//更新答案(因为含#,所以不用乘2)
}
cout<<ans-1<<"\\n";
return 0;
}
AcWing 158. 项链
//题意:给出两个字符串,判断他们串成环后是否等价,输出最小字典序表示法,n=1e6
//思路:如果两个字符串是等价的话,那么他们的最小表示肯定一样的,所以直接O(n)求最小表示即可
//最小表示法:朴素O(n^2)做法为每次比较i与j的循环同构,把当前比较到的位置记作k,每次遇到不一样的字符时便把大的跳过,然后重新令k=0开始比较。此时可以发现性质,当不一样时,如果s[i+k]>s[j+k],对于起点在[i,i+k]内的字符串s[i+p],都不可能优于s[j+p],所以可以直接跳到s[i+k-1]继续与s[j]比较,此时优化到O(n)。
#include<bits/stdc++.h>
using namespace std;
string calc(string s){
int n = s.size(); s += s;
int i=0, j=1, k; //双指针,匹配长度
while(i<n && j<n){
for(k=0; k<n&&s[i+k]==s[j+k]; k++);
if(s[i+k]>s[j+k])i += k+1;
else j += k+1;
if(i==j)i++;
}
int ans = min(i,j);
return s.substr(ans, n);
}
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
string a, b; cin>>a>>b;
string ta=calc(a), tb=calc(b);
if(ta==tb)cout<<"Yes\\n"<<ta<<"\\n";
else cout<<"No\\n";
return 0;
}
AcWing 516. 神奇的幻方
#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 50;
int a[maxn][maxn];
int main(){
memset(a,0,sizeof(a));//数组清零
int n; cin>>n;
int x = 1, y = (n+1)/2, tot = n*n;//中间位置
a[x][y] = 1;
for(int k = 2; k <= tot; k++){
if(x==1 && y!=n){
a[n][y+1] = k;//注意k++
x = n, y = y+1;
continue;//x,y值改变了要continue
}
if(x!=1 && y==n){//条件看错
a[x-1][1] = k;
x = x-1, y = 1;
continue;
}
if(x==1 && y==n){
a[x+1][y] = k;
x = x+1, y = y;
continue;
}
if(x!=1 && y!=n){
if(a[x-1][y+1]==0){
a[x-1][y+1] = k;
x = x-1, y = y+1;
}else{
a[x+1][y] = k;
x = x+1, y = y;
}
continue;
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cout<<a[i][j]<<" ";
}
cout<<'\\n';
}
return 0;
}
AcWing 2268. 时态同步
//题意:给出一颗n个点的树(1e5),每条边有一个权值c, 每次操作可以令某个权值+1,求最少操作次数令根节点到每个叶节点路径上的权值和相等
//思路:容易发现,越靠近根节点的,调整代价越小。我们可以把节点深度类比成距离,题目即为求把所有叶子节点调整到同一高度,每次优先调整靠近根部的。先dfs一遍更新到最远叶节点的距离dis[x],再循环一遍更新调整其余子节点的距离跟它一样。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 5e5+10;
struct node{int to,w;};
vector<node>G[maxn];
LL dis[maxn], ans;
void dfs(int x, int fa){
for(auto y : G[x]){
if(y.to==fa)continue;
dfs(y.to,x);
dis[x] = max(dis[x], dis[y.to]+y.w);//更新这棵子树根节点和最远叶子节点的距离
}
for(auto y : G[x]){
if(y.to==fa)continue;
ans += dis[x]-(dis[y.to]+y.w);//调整每个子节点到最远的距离,累加每次调整的代价
}
}
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, rt; cin>>n>>rt;
for(int i = 1; i < n; i++){
int x,y,z; cin>>x>>以上是关于AcWing进阶算法课Level-4 第七章 基础算法的主要内容,如果未能解决你的问题,请参考以下文章