鸽了一天。。不要在意(反正也没人看
1.一个有趣的idea(不知道有没有题)
给一张图,每个边有边权,现在要求log时间内求出:
给出x值和k点,去除大于x的所有边后k连通块的信息(sum啊max啊等等线段树干的事)
做法:首先跑最小生成树,我们建立一棵新树,如果最小生成树连接了u和v,建立新点p,连接p,fa[u]和p,fa[v],并且把p作为新的连通块的父亲节点
这样就把连通块上询问的信息变到子树上询问了
2.ZOJ - 3949
题意:给出一棵树,再建一条边,使得
∑ni=2dis(1,i)
∑
i
=
2
n
d
i
s
(
1
,
i
)
最小
做法:我们考虑直接求出另外一个点在x的贡献
我们得知了这个点在x父亲的贡献
考虑到x,x到1号节点路径上有一个节点的最优路径会改变
X的儿子贡献会改变
X的父亲以及父亲的父亲等等的子树贡献会改变
我们发现这些改变都是等差数列
分别维护即可
//Copyright(c)2017 Mstdream
#include<bits/stdc++.h>
using namespace std;
#define LL long long
inline void splay(LL &v){
v=0;char c=0;LL p=1;
while(c<'0' || c>'9'){if(c=='-')p=-1;c=getchar();}
while(c>='0' && c<='9'){v=(v<<3)+(v<<1)+c-'0';c=getchar();}
v*=p;
}
const LL N=400010;
LL nxt[N],fir[N],sz,n,siz[N],fa[N],to[N],dep[N],ans,tot,f[N];
void add(LL x,LL y){
nxt[++sz]=fir[x],fir[x]=sz,to[sz]=y;
}
void init(){
for(LL i=1;i<=n*2;i++){
siz[i]=nxt[i]=fir[i]=to[i]=0;
fa[i]=dep[i]=f[i]=0;
}
sz=0;
}
void dfs1(LL x,LL f){
fa[x]=f;siz[x]=1;dep[x]=dep[f]+1;
for(LL u=fir[x];u;u=nxt[u]){
if(to[u]!=fa[x]){
dfs1(to[u],x);
siz[x]+=siz[to[u]];
}
}
}
void dfs2(LL x,LL pre,LL ooo){
if(dep[x]>1){
LL ret=tot;
ret-=siz[x]*(dep[x]-2);
ret-=pre;
ans=min(ans,ret);
}
for(LL u=fir[x];u;u=nxt[u]){
if(to[u]!=fa[x]){
if(dep[x]<4){
dfs2(to[u],0,0);
}
else{
f[dep[x]]=siz[x]-siz[to[u]];
LL now=(siz[x]-siz[to[u]])*(dep[x]-3);
if(dep[x]&1)dfs2(to[u],pre-ooo+now,ooo-f[(dep[x]+3)/2]+siz[x]-siz[to[u]]);
else dfs2(to[u],pre-ooo+now,ooo+siz[x]-siz[to[u]]);
}
}
}
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
init();splay(n);
for(LL i=1;i<n;i++){
LL x,y;splay(x),splay(y);
add(x,y),add(y,x);
}
dfs1(1,0);
tot=0;
for(LL i=1;i<=n;i++){
tot+=dep[i]-1;
}
ans=tot;
dfs2(1,0,0);
cout<<ans<<endl;
}
3.idea*2
题意:给出一张地图,每个点需要
Wi
W
i
个士兵才能攻占,每个边需要
Vi
V
i
个士兵才能攻占,士兵攻占完后可以沿着攻占完了的边走动,假设士兵没有损伤,一开始可以向任意点空降任意数量的士兵(每个点空降的单位代价不同),求最小代价
做法:首先士兵沿着最小生成树行走一定最优
我们对每个节点记录a,b,v三个值域
a代表占领当前连通块所需要的最少士兵数
b代表占领当前连通块所经过的代价最大的边
v代表占领当前连通块的最小代价
每次合并,v用a*b更新一下即可
//Copyright(c)2017 Mstdream
#include<bits/stdc++.h>
using namespace std;
#define LL long long
inline void splay(int &v){
v=0;char c=0;int p=1;
while(c<'0' || c>'9'){if(c=='-')p=-1;c=getchar();}
while(c>='0' && c<='9'){v=(v<<3)+(v<<1)+c-'0';c=getchar();}
v*=p;
}
const int N=300010;
int a[N],b[N],fa[N],n,m;
LL v[N],ans;
int getfa(int x){
if(fa[x]==x)return x;
return fa[x]=getfa(fa[x]);
}
struct E{
int l,r,w;
bool operator<(const E&x)const{
return w<x.w;
}
}e[N];
int main(){
splay(n),splay(m);
for(int i=1;i<=n;i++){
splay(a[i]),splay(b[i]);
v[i]=(LL)a[i]*b[i];
fa[i]=i;
}
for(int i=1;i<=m;i++){
splay(e[i].l),splay(e[i].r),splay(e[i].w);
}
sort(e+1,e+m+1);
for(int i=1;i<=m;i++){
int x=getfa(e[i].l),y=getfa(e[i].r);
if(x!=y){
fa[x]=y;
a[y]=max(e[i].w,max(a[x],a[y]));
b[y]=min(b[x],b[y]);
v[y]=min((LL)a[y]*b[y],v[x]+v[y]);
}
}
for(int i=1;i<=n;i++)if(fa[i]==i)ans+=v[i];
cout<<ans<<endl;
}
4.scppc2016 problemH
题意:定义广义pascal三角:如三维的pascal三角为
a[i][j][k]=a[i−1][j][k]+a[i][j−1][k]+a[i][j][k−1]
a
[
i
]
[
j
]
[
k
]
=
a
[
i
−
1
]
[
j
]
[
k
]
+
a
[
i
]
[
j
−
1
]
[
k
]
+
a
[
i
]
[
j
]
[
k
−
1
]
求所有维数加起来等于x的值有哪些(比如三维就是
i+j+k==x
i
+
j
+
k
==
x
)
做法:二维pascal三角中每个数是一个组合数,可以看做是
(1,1)
(
1
,
1
)
到
(n,m)
(
n
,
m
)
的路径数。
三维中也可以看做
(1,1,1)
(
1
,
1
,
1
)
到
(n,m,k)
(
n
,
m
,
k
)
的方案数
所以我们会求某个点的值了
观察发现交换某两位的数答案不变,我们规定必须数值必须从大到小
这样就可以搜索了
//Copyright(c)2017 Mstdream
#include<bits/stdc++.h>
using namespace std;
#define LL unsigned long long
LL ans[10000000],c[99][99];
LL t,d,n;
LL a[55];
void dfs(LL now,LL hs){
if(now==d){
a[now]=hs;
LL x=n;LL p=1;
for(LL i=1;i<=d;i++){
p*=c[x][a[i]];
x-=a[i];
}
ans[++t]=p;
return;
}
for(LL i=a[now-1];(d-now+1)*i<=hs;i++){
a[now]=i;dfs(now+1,hs-i);
}
}
int main(){
//freopen("xxx.in","r",stdin);
c[0][0]=1;
for(LL i=1;i<=55;i++){
c[i][0]=1;
for(LL j=1;j<=i;j++){
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
}
cin>>d>>n;n--;
dfs(1,n);
sort(ans+1,ans+t+1);
t=unique(ans+1,ans+t+1)-ans-1;
for(LL i=1;i<=t;i++){
printf("%I64u\n",ans[i]);
}
}