21.3.27上午数据结构
所有题不卡long long,数据范围均要求使用\(O(n\ log\ n)\)
T1
题目大意
给定一棵有\(n\)个节点的树和\(m\)个操作,操作有两个:
1.\(1\ x\),询问节点\(x\)的子树异或和
2.\(2\ x\ num\),将节点\(x\)的数值更改为\(num\)
solution
很基础的数据结构题。对于每个节点,记录它的数值、子树异或和以及父节点。
在建树的过程中返回当前节点的子树异或和,那么子树异或和的维护方法就是把每个子节点的子树异或和异或一遍再异或该节点本身数值即可。
\(\because (a\;xor\;b)\;xor\;b\ =\ a\)所以对于每次修改操作,只需要将该节点以及其先辈节点的子树异或和对原数和新数取一遍异或即可。询问操作直接输出。
然后就是我很丑的代码:
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)-'0'+c,c=getchar();
return x*f;
}
int n,m;
struct node
{
int xorn,fa,num;
}tr[100010];
struct edge
{
int to,next;
}e[200010];
int h[100010],ei;
bool vis[100010];
inline void add(int x,int y)
{
e[++ei]=(edge){y,h[x]};h[x]=ei;
}
inline int build(int rt)
{
for(int i=h[rt];i;i=e[i].next)
{
if(!vis[e[i].to])
{
vis[e[i].to]=1;
tr[e[i].to].fa=rt;
tr[rt].xorn^=build(e[i].to);
}
}
tr[rt].xorn^=tr[rt].num;
return tr[rt].xorn;
}
inline void update(int p,int ne,int ori)
{
if(!p)return;
tr[p].xorn=tr[p].xorn^ori^ne;
update(tr[p].fa,ne,ori);
return;
}
int main()
{
freopen("xorsum.in","r",stdin);
freopen("xorsum.out","w",stdout);
n=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
add(x,y);add(y,x);
}
for(int i=1;i<=n;i++)tr[i].num=read();
vis[1]=1;
build(1);
m=read();
for(int i=1;i<=m;i++)
{
int x=read(),y=read(),z;
if(x==1)
{
printf("%d\n",tr[y].xorn);
}else
{
z=read();
update(y,z,tr[y].num);
tr[y].num=z;
}
}
return 0;
}
我个憨批把z写成1爆0了
T2
题目大意
给定一个数\(n\)和\(n\)个字符串\(S_1,S_2,…,S_n\)\ 求
\(\sum_{i=1}^n \sum_{j=i+1}^n\ LCP(S_i\ ,\ S_j)\)。\(LCP(S_i\ ,\ S_j)\)表示\(S_i\)和\(S_j\)的最长公共前缀长。
暴力
很好想,直接把每个字符串\(S_i\)存下来然后暴力比较即可,时间复杂度\(O(n^2\,lenS)\)
solution
望着第三题发呆的时候突然想到这题就是一道\(Trie\)树板子题。
对于每次输入的字符串\(S_i\),将它更新至字典树当中。字典树上每个节点维护其包含的单词个数(即被访问次数),每访问一次将\(ans\)加上该数并将这个数\(++\)。其余\(Trie\)树板子。
依然很丑的代码:
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)-'0'+c,c=getchar();
return x*f;
}
int n,ll;
char s1[1000010];
struct node
{
int num;
int son[27];
}tr[1000010];
int gpc,ans;
inline void update(int rt,int le)
{
if(le>ll)return;
if(le)ans+=tr[rt].num;
tr[rt].num++;
if(!tr[rt].son[s1[le]-'a'])
{
tr[rt].son[s1[le]-'a']=++gpc;
}
update(tr[rt].son[s1[le]-'a'],le+1);
return;
}
int main()
{
freopen("lcpsum.in","r",stdin);
freopen("lcpsum.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)
{
scanf("%s",s1);
ll=strlen(s1);
update(0,0);
}
printf("%d\n",ans);
}
T3
题目大意
给定一棵带边权的树求其直径(即树上两点之间距离最大值)
solution
不会,一数据结构我写了个\(Floyd\)就爬了
思考的时候感觉可以先维护\(LCA\)然后遍历从根节点到每个节点的距离最后每两个节点\(i、j\)距离就是\(dis[i]+dis[j]-2*dis[LCA[i][j]]\)
但是发现时间复杂度是\(O(n^2)\)所以就没写。
两遍BFS就做完了。
又是很丑的代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)-'0'+c,c=getchar();
return x*f;
}
int T,n;
struct edge
{
int to,next,v;
}e[1000010];
int ei,h[1000010];
queue<int>qu;
int dis[1000010];
inline void add(int x,int y,int v)
{
e[++ei]=(edge){y,h[x],v};
h[x]=ei;return;
}
inline int bfs(int rt)
{
for(int i=1;i<=n;i++)dis[i]=1000000000000000;
dis[rt]=0;
qu.push(rt);
while(!qu.empty())
{
int x=qu.front();
qu.pop();
for(int i=h[x];i;i=e[i].next)
{
int y=e[i].to,v=e[i].v;
if(dis[y]>=1000000000000000)dis[y]=dis[x]+v,qu.push(y);
}
}
int ans=1;
for(int i=2;i<=n;i++)
{
if(dis[i]>dis[ans])ans=i;
}
return ans;
}
signed main()
{
freopen("diameter.in","r",stdin);
freopen("diameter.out","w",stdout);
T=read();
while(T--)
{
for(int i=1;i<=n;i++)h[i]=0;
ei=0;
n=read();
for(int i=1;i<n;i++)
{
int x,y,z;
x=read(),y=read(),z=read();
add(x,y,z);
add(y,x,z);
}
int to=bfs(1);
to=bfs(to);
printf("%lld\n",dis[to]);
}
}

浙公网安备 33010602011771号