题目:见2013-5 day2
QAQ大滚粗。。。
A
对于30%的数据,是bzoj1016的加强版,我太弱了只会这个。。。满分算法待研究。。。
50分状压DP,不嫌麻烦可以写一写,,,100%的算法不敢看“基于欧几里得的迭代消元”神马东西。。。。。。。。。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
#define rep(i,l,r) for(int i=(l),_=(r);i<=_;i++)
#define per(i,r,l) for(int i=(r),_=(l);i>=_;i--)
#define MS(arr,x) memset(arr,x,sizeof(arr))
#define INE(i,u) for(int i=head[u];~i;i=e[i].next)
#define LL long long
inline const int read()
{int r=0,k=1;char c=getchar();for(;c<'0'||c>'9';c=getchar())if(c=='-')k=-1;
for(;c>='0'&&c<='9';c=getchar())r=r*10+c-'0';return k*r;}
////////////////////////////////////////////////
const int N=110;
const int M=10010;
int n,m,p,k;
struct edge{int u,v,w;}e[M];
int fa[N];
struct data{int l,r,cnt;}a[M];
int cnt=0;
LL ans=1;
int curw;
////////////////////////////////////////////////
bool cmp(edge x,edge y){return x.w<y.w;}
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int find2(int x){return x==fa[x]?x:find2(fa[x]);}
bool unio(int u,int v)
{
int fu=find(u),fv=find(v);
if(fu==fv) return 0;
return fa[fu]=fv,1;
}
int dfs(int cur,int cnt)
{
int res=0;
if(cur == a[curw].r+1)
{
if(cnt==a[curw].cnt) res++;
return res;
}
res+=dfs(cur+1,cnt);
int fu=find2(e[cur].u),fv=find2(e[cur].v);
if(fu!=fv)
{
fa[fu]=fv; cnt++;
res+=dfs(cur+1,cnt);
fa[fu]=fu;
}
return res;
}
////////////////////////////////////////////////
void input()
{
n=read(); m=read(); p=read();
rep(i,1,m) e[i]=(edge){read(),read(),read()};
sort(&e[1],&e[m+1],cmp);
}
void solve()
{
rep(i,1,n) fa[i]=i;
int blk=n;
cnt=1;
rep(i,1,m)
{
if(e[i].w!=e[i-1].w)
{
a[cnt].r=i-1;
cnt++;
a[cnt].l=i;
}
if(unio(e[i].u,e[i].v)) blk--,a[cnt].cnt++;
if(blk==1) break;
}
for(a[cnt].r=a[cnt].l;e[a[cnt].l].w == e[a[cnt].r+1].w;a[cnt].r++);
rep(i,1,n) fa[i]=i;
blk=n;
rep(i,1,cnt)
{
curw=i;
ans=ans*dfs(a[i].l,0)%p;
rep(j,1,n) fa[j]=j;
rep(j,1,a[i].r) unio(e[j].u,e[j].v);
}
cout<<ans<<endl;
}
////////////////////////////////////////////////
int main()
{
freopen("mst.in","r",stdin); freopen("mst.out","w",stdout);
input(),solve();
return 0;
}
B
这题秒了,第一感觉就是贪心,也比较好证
考虑如果有一条最大的链没选的话,那么他公共部分与他最长的那条链,交换肯定更优
题解上说,用费用流是不退流的,所以可以贪心,我还是没看到本质啊。。。。。。
然后用dfs序+线段树维护就可以了
有个地方写麻烦了,我用并查集实现压缩路径,事实上每次取的都是到根的,遇到0就可以终止
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <stack>
using namespace std;
#define rep(i,l,r) for(int i=(l),_=(r);i<=_;i++)
#define per(i,r,l) for(int i=(r),_=(l);i>=_;i--)
#define MS(arr,x) memset(arr,x,sizeof(arr))
#define INE(i,u) for(int i=head[u];~i;i=e[i].next)
#define LL long long
typedef pair<LL,int> pli;
inline const int read()
{int r=0,k=1;char c=getchar();for(;c<'0'||c>'9';c=getchar())if(c=='-')k=-1;
for(;c>='0'&&c<='9';c=getchar())r=r*10+c-'0';return k*r;}
////////////////////////////////////////////////
const int N=200010;
int n,k;
struct edge{int v,next;}e[2*N];
int head[N],kk;
int w[N],fa[N];
int in[N],out[N],ind,_in[N];
pli mx[N*4];
LL dis[N];
LL lz[N*4];
////////////////////////////////////////////////
namespace UFS
{////////////////////////////
int f[N];
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
}////////////////////////////
void adde(int u,int v){e[kk]=(edge){v,head[u]};head[u]=kk++;}
void dfs()
{
static stack<int>s;
s.push(1);
while(!s.empty())
{
int u=s.top(),f=0;
if(!in[u]) in[u]=++ind;
INE(i,u)
{
int v=e[i].v; if(v==fa[u] || in[v]) continue;
dis[v]=dis[u]+w[v];
fa[v]=u;
s.push(v);
f=1;
}
if(!f)
{
out[u]=ind;
INE(i,u)
{
int v=e[i].v; if(v==fa[u]) continue;
}
s.pop();
}
}
rep(i,1,n) _in[in[i]]=i;
}
#define LS o<<1,L,mid
#define RS o<<1|1,mid+1,R
void pushdown(int o)
{
if(lz[o])
{
mx[o<<1].first+=lz[o];
mx[o<<1|1].first+=lz[o];
lz[o<<1]+=lz[o];
lz[o<<1|1]+=lz[o];
lz[o]=0;
}
}
void build(int o,int L,int R)
{
if(L==R)
{
mx[o].first=dis[_in[L]];
mx[o].second=L;
return;
}
int mid=L+R>>1;
build(LS); build(RS);
mx[o]=max(mx[o<<1],mx[o<<1|1]);
}
void add(int l,int r,LL x,int o,int L,int R)
{
if(l<=L && R<=r)
{
mx[o].first+=x;
lz[o]+=x;
return;
}
pushdown(o);
int mid=L+R>>1;
if(r<=mid) add(l,r,x,LS);
else if(l>mid) add(l,r,x,RS);
else add(l,mid,x,LS), add(mid+1,r,x,RS);
mx[o]=max(mx[o<<1],mx[o<<1|1]);
}
pli getmax(int l,int r,int o,int L,int R)
{
if(l<=L && R<=r) return mx[o];
pushdown(o);
int mid=L+R>>1;
if(r<=mid) return getmax(l,r,LS);
else if(l>mid) return getmax(l,r,RS);
else return max(getmax(l,mid,LS),getmax(mid+1,r,RS));
}
////////////////////////////////////////////////
void input()
{
MS(head,-1);
n=read(); k=read();
rep(i,1,n) w[i]=read();
rep(i,2,n)
{
int u=read(),v=read();
adde(u,v); adde(v,u);
}
}
void solve()
{
dis[1]=w[1]; dfs();
build(1,1,n);
rep(i,1,n) UFS::f[i]=i;
LL ans=0;
rep(i,1,k)
{
pli ret=getmax(1,n,1,1,n);
ans+=ret.first;
int u=_in[ret.second];
using namespace UFS;
for(;u;u=f[u])
{
add(in[u],out[u],-w[u],1,1,n);
f[u]=find(fa[u]);
}
}
cout<<ans<<endl;
}
////////////////////////////////////////////////
int main()
{
freopen("galgame.in","r",stdin); freopen("galgame.out","w",stdout);
input(),solve();
return 0;
}
C
这题我遇到过4次了,是一道神题,没敢碰。。。