T1:
A 求和
时间限制: 1 Sec 空间限制: 256 MB
输入输出文件名:A.in,A.out
题目描述
给出一棵以1为根的有n个节点的树,树上每条边都有其边权。
求所有点对之间的路径上的边权和的总和。
输入格式:
第一行为n
接下来n-1行,每行三个整数,分别表示一条边的两端点编号和边权。(编号为1..n)
输出格式:
输出一个数字表示总和
输入样例
4
1 2 10
2 3 10
1 4 20
输出样例
130
样例解释
1->2:10 , 1->3:20 , 1->4:20 , 2->3:10 , 2->4:30 , 3->4:40 , 总和为130。
数据范围
对于30%的数据,1<=n<=300
对于80%的数据,1<=n<=3000
对于100%的数据,1<=n<=100000,边权为<=100的正整数。
这题的做法是考虑每条边对答案的贡献(套路题QAQ)
即考虑有多少条路径经过这条边
易知即边左边的点的数目*另一边的点的数目
我们可以先从根节点dfs一遍求出每个节点的子树的大小
即一边的点的数目
这样就可以得到贡献为 cost*size[e[k].to]*(n-size[e[k].to])
#include<cstdio>
#include<cstring>
const int N=100005;
struct node
{
int to,next,c;
}e[N*2];
int first[N],visit[N],size[N];
int cnt=0;
int n;
void insert(int u,int v,int c)
{
e[++cnt].to=v;e[cnt].next=first[u];first[u]=cnt;e[cnt].c=c;
e[++cnt].to=u;e[cnt].next=first[v];first[v]=cnt;e[cnt].c=c;
}
long long ans=0;long long sum=0;
void dfs(int x)
{
visit[x]=1;size[x]=1;
for(int k=first[x];k;k=e[k].next)
if(!visit[e[k].to])
{
dfs(e[k].to);
size[x]+=size[e[k].to];
}
}
void dfs2(int x)
{
visit[x]=1;
for(int k=first[x];k;k=e[k].next)
{
if(!visit[e[k].to])
{
ans+=e[k].c*size[e[k].to]*(n-size[e[k].to]);
dfs2(e[k].to);
}
}
}
int main()
{
scanf("%d",&n);
int u,v,c;
for(int i=1;i<n;i++)
{
scanf("%d %d %d",&u,&v,&c);
insert(u,v,c);
}
dfs(1);
memset(visit,0,sizeof(visit));
dfs2(1);
printf("%lld\n",ans);
return 0;
}
T2:
Xor路
(xor.pas/c/cpp) 128MB 1s
给定一棵有N个点和N-1条边的树,请你求出树中的最长路径,以及总共有多少条最长路径。
这里路径长度是用xor定义的,即若经过的边的权值为a1, a2, a3,...,an,则这条路径的总权值为 a1 xor a2 xor a3 ... xor an。
输入格式
第1行为一个正整数 N,为点的个数。
第2行至第N行,每行包含三个正整数x,y,z,表示x和y之间有一条权值为z的边。
输出格式
仅一行,包含两个数字,为最长路径的长度和条数。
样例输入
4
1 2 3
2 4 1
1 3 4
样例输出
7 1
样例解释
2-1-3 这条路径,长度为3 xor 4=7。
数据范围
全部数据满足 0<=z<=1e9
(请注意栈空间溢出)
由xor的性质可得
两个点的路径即为根节点到一个点的路径^根节点到另一个点的路径
然后又由xor的性质可得
高位上两个值的二进制表示不同
异或的结果就为1(这样是最优的)
所以想到建一颗trie树
维护对一个值的最优异或结果
并统计方案数
#include<cstdio>
#include<cstring>
const int N=200005;
struct node
{
int next,to,c;
}e[N*2];
int qu[N+100],dis[N];int cnt=0;int first[N],visit[N];
int l[N*31][2];
int h[N*31];
void insert(int u,int v,int c)
{
e[++cnt].to=v;e[cnt].next=first[u];e[cnt].c=c;first[u]=cnt;
e[++cnt].to=u;e[cnt].next=first[v];e[cnt].c=c;first[v]=cnt;
}
void bfs()
{
int head=1,tail=2;
qu[1]=1;
while(head<=tail)
{
int rr=qu[head++];visit[rr]=1;
for(int k=first[rr];k;k=e[k].next)
if(!visit[e[k].to])
visit[e[k].to]=1,qu[tail++]=e[k].to,dis[e[k].to]=dis[rr]^e[k].c;
}
}
int k=0;
int tot=0;
void insert(int x)
{
int ro=0;
for(int i=30;i>=0;i--)
{
int cnt=(x&(1<<i))>>i;
if(!l[ro][cnt]) l[ro][cnt]=++tot;
ro=l[ro][cnt];
}
h[ro]++;
}
int find(int x)
{
int ro=0;
for(int i=30;i>=0;i--)
{
int cnt=(x&(1<<i))>>i;
if(l[ro][!cnt]) ro=l[ro][!cnt],k+=(1<<i);
else ro=l[ro][cnt];
}
return h[ro];
}
int main()
{
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
int n;
scanf("%d",&n);
int u,v,c;
for(int i=1;i<n;i++)
scanf("%d %d %d",&u,&v,&c),insert(u,v,c);
bfs();
for(int i=1;i<=n;i++) insert(dis[i]);
long long int max=0,sum=0;
for(int i=1;i<=n;i++)
{
k=0;int p=find(dis[i]);
if(k>max) max=k,sum=p;
else if(k==max) sum+=p;
}
printf("%lld %lld\n",max,sum>>1);
fclose(stdin);
fclose(stdout);
return 0;
}
T3
时间限制: 1 Sec 空间限制: 256 MB
输入输出文件名:B.in,B.out
题目描述
给出一棵以1为根的有n个节点的树,树上每条边都有其边权。
四条琉璃想选择一个点作为起点,他希望这个起点到其他所有点距离和是最小的。
输入格式:
第一行为n
接下来n-1行,每行三个整数,分别表示一条边的两端点编号和边权。(编号为1..n)
输出格式:
一个数,表示那个最小的距离和。
输入样例
5
1 2 10
2 3 10
1 4 20
2 5 10
输出样例
60
样例解释
选2作为起点。2->1:10,2->3:10,2->4:30,2->5:10,总和为60
数据范围
对于40%的数据,1<=n,m<=2333
对于100%的数据,1<=n,m<=300 000,边权为<=100的正整数
这道题很多种方法可以做
方法一:
发现可以O(1)转移
先求出一个节点的答案
然后从一个点转移到另一个点
就+e[k].c*(n-size[e[k].to])-e[k].c*size[e[k].to]
其实就是求的树的重心
方法二:s
双向树形dp
第一次dfs从儿子上传信息到父亲
即维护一个点到它的子树内每个点的距离之和(big数组)
第二次dfs从父亲下传信息到儿子
即维护到子树外的点的距离之和(f数组)(父亲和兄弟)
转移方程看代码吧
#include<cstdio>
#include<cstring>
const int N=300050;
struct node
{
int next,to,c;
}e[N*2];
int first[N];
int cnt=0;
long long int f[N];
int n;
void insert(int u,int v,int c)
{
e[++cnt]=(node){first[u],v,c};first[u]=cnt;
e[++cnt]=(node){first[v],u,c};first[v]=cnt;
}
long long int big[N];
int size[N],visit[N];
void dfs1(int x)
{
visit[x]=1;size[x]=1;
for(int k=first[x];k;k=e[k].next)
if(!visit[e[k].to])
{
dfs1(e[k].to);
size[x]+=size[e[k].to];
big[x]+=big[e[k].to]+(long long )e[k].c*size[e[k].to];
}
}
void dfs2(int x)
{
visit[x]=1;
for(int k=first[x];k;k=e[k].next)
if(!visit[e[k].to])
{
f[e[k].to]=f[x]+(big[x]-big[e[k].to]-(long long )size[e[k].to]*e[k].c)+(n-size[e[k].to])*(long long )e[k].c;
dfs2(e[k].to);
}
}
int main()
{
freopen("B.in","r",stdin);
freopen("B.out","w",stdout);
scanf("%d",&n);
int u,v,c;
for(int i=1;i<n;i++)
{
scanf("%d %d %d",&u,&v,&c);
insert(u,v,c);
}
dfs1(1);
memset(visit,0,sizeof(visit));
dfs2(1);
long long min=1e17;
for(int i=1;i<=n;i++)
min=min<big[i]+f[i]?min:big[i]+f[i];
printf("%lld\n",min);
return 0;
}
T4:
题面因为某些原因不能搬啦
注意到路径的一端肯定是叶子
所以套路其实就跟T1一样啦
算一下子树内的叶子数就可以了
然后注意根节点如果只有一个儿子也可以是路径的终点(其实就跟叶子一样啦)
#include<cstdio>
#include<cstring>
const int N=100070;
struct node
{
int to,next,c;
}e[N*2];
int cnt;
int first[N];
void insert(int u,int v,int c)
{
e[++cnt]=(node){v,first[u],c};first[u]=cnt;
e[++cnt]=(node){u,first[v],c};first[v]=cnt;
}
int read()
{
int ans=0;char t=getchar();
while(t<'0'||t>'9') t=getchar();
while(t>='0'&&t<='9') ans=ans*10+t-'0',t=getchar();
return ans;
}
int visit[N],size[N],leave[N];
void dfs(int x)
{
visit[x]=1;size[x]=1;
int sum=0;
for(int k=first[x];k;k=e[k].next)
if(!visit[e[k].to])
{
sum++;
dfs(e[k].to);
size[x]+=size[e[k].to];
leave[x]+=leave[e[k].to];
}
if(x==1&&sum==1) leave[x]+=1;
if(!sum) leave[x]=1;
}
unsigned long long ans=0;
int n,m;
void dfs2(int x)
{
visit[x]=1;
for(int k=first[x];k;k=e[k].next)
if(!visit[e[k].to])
{
ans+=(unsigned long long)leave[e[k].to]*(n-size[e[k].to])*e[k].c;
ans+=(unsigned long long)(leave[1]-leave[e[k].to])*size[e[k].to]*e[k].c;
dfs2(e[k].to);
}
}
int main()
{
freopen("senso.in","r",stdin);
freopen("senso.out","w",stdout);
n=read(),m=read();
int l,u,v;
for(int i=1;i<=m;i++)
{
l=read(),u=read(),v=read();
insert(u,v,l);
}
dfs(1);
memset(visit,0,sizeof(visit));
dfs2(1);
printf("%llu\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
T5:
异或又来辣
求树上两点之间的路径的异或和
如果没有修改直接dfs就可以了
如果加上点修改
那么注意到修改一个点只会对该点的子树的dis产生影响
而子树的dfs序是连续的
所以其实不用树链剖分
直接线段树就可以了
#include<cstdio>
#include<cstring>
#define lson ro<<1
#define rson ro<<1|1
const int N=500090,mod=100000007;
struct edgt
{
int to,next,v;
}e[N<<1];
int read()
{
int ans=0;char t=getchar();
while(t<'0'||t>'9') t=getchar();
while(t>='0'&&t<='9') ans=ans*10+t-'0',t=getchar();
return ans;
}
int cnt;
int first[N];
void insert(int u,int v,int c)
{
e[++cnt]=(edgt){v,first[u],c};first[u]=cnt;
e[++cnt]=(edgt){u,first[v],c};first[v]=cnt;
}
int visit[N],dis[N];
void dfs(int x)
{
visit[x]=1;
for(int k=first[x];k;k=e[k].next)
if(!visit[e[k].to])
{
dis[e[k].to]=dis[x]^e[k].v;
dfs(e[k].to);
}
}
int a[30];
long long ans[(1<<20)+7];
int main()
{
freopen("mahou.in","r",stdin);
freopen("mahou.out","w",stdout);
int n=read(),q=read(),k=read();
int u,v,c;
for(int i=1;i<n;i++)
{
u=read(),v=read(),c=read();
insert(u,v,1<<(c-1));
}
for(int i=1;i<=k;i++) a[i]=read();
int top=(1<<k)-1;
ans[0]=1;
for(int i=1;i<=top;i++)
{
int j=i,count=1;
ans[i]=1;
while(j)
{
if(j&1) ans[i]=ans[i]*(long long)a[count]%mod;
j=j>>1;count++;
}
}
dfs(1);
for(int i=1;i<=q;i++)
{
u=read(),v=read();
printf("%lld\n",ans[dis[u]^dis[v]]);
}
fclose(stdin);
fclose(stdout);
return 0;
}