省选模拟13
挂了好多好多分,暴力打满了有130分
而我只拿了35分,让我非常的生气
第一题,一眼网络流,于是开打,后来假了,但是能过第二个包,第一个包打了一个五维DP
第二题,研究了一会发现可以DP,然而我的状态中又设计了许许多多没用的
没优化成功,于是复杂度是\(\mathcal{O(n^2)}\),其实可以省去很多状态,一下子就变log了
第三题,发现对点的关系建图后可以缩点,没有入度的点的个数就是答案
T1 一般图带权多重匹配
发现网络流的瓶颈在于无法限制自己和自己必须是偶数
那我们先考虑所有的点权都是偶数的情况
我们把每一次消掉的两个点进行连边,这样由于每个点都连了偶数条边
每个点的入度必然等于出度,我们可以用好多的欧拉回路来覆盖这个图
于是乎偶数的似乎可以直接将每个点拆成一半放左边,另一半放右边
那么我们就可以跑网络流了
如果有奇数的呢?那就再加几条欧拉路,也就是说我枚举每个奇数点是入度还是出度
这样由于点数非常的小,所以可以枚举
AC_code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*t;
}
const int inf=0x3f3f3f3f;
const int N=55;
int n,a[N],c[N][N],s=101,t=102;
struct E{int to,nxt,val,cot;}e[(N*N+N*2)*2],d[(N*N+N*2)*2];
int head[N*2],rp=1;
void add_edg(int x,int y,int v,int c){
e[++rp].to=y;e[rp].val=v;e[rp].cot=c;
e[rp].nxt=head[x];head[x]=rp;
}
int dis[N*2],flo[N*2],pre[N*2],epr[N*2];
bool vis[N*2];
bool spfa(){
memset(dis,0x3f,sizeof(dis));
queue<int> q;while(!q.empty())q.pop();
q.push(s);vis[s]=true;dis[s]=0;flo[s]=inf;
while(!q.empty()){
int x=q.front();q.pop();vis[x]=false;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(!e[i].val||dis[y]<=dis[x]+e[i].cot)continue;
dis[y]=dis[x]+e[i].cot;
flo[y]=min(flo[x],e[i].val);
pre[y]=x;epr[y]=i;
if(!vis[y])q.push(y),vis[y]=true;
}
}
return dis[t]!=inf;
}
int sum,cst,sm,fl,ans;
int pos[N],p1[N],p2[N];
signed main(){
freopen("match.in","r",stdin);
freopen("match.out","w",stdout);
n=read();
bool fl1=true;
fo(i,1,n){
a[i]=read();sm+=a[i];
if(a[i]>10)fl1=false;
if(a[i]&1)fl++;
add_edg(s,i,a[i]>>1,0);
if(a[i]&1)p1[fl]=rp;
add_edg(i,s,0,0);
add_edg(i+n,t,a[i]>>1,0);
if(a[i]&1)p2[fl]=rp;
add_edg(t,i+n,0,0);
}
fo(i,1,n)fo(j,1,n)c[i][j]=read();
fo(i,1,n)fo(j,i,n)c[i][j]=c[j][i]=min(c[i][j],c[j][i]);
fo(i,1,n)fo(j,1,n){
add_edg(i,j+n,inf,c[i][j]);
add_edg(j+n,i,0,-c[i][j]);
}
if(fl&1){printf("-1");return 0;}
memcpy(d,e,sizeof(e));
int u=(1<<fl)-1;ans=inf;
//cout<<"ZZ"<<endl;
fo(ss,0,u){
memcpy(e,d,sizeof(d));
int so=0;
fo(i,1,fl){
if(ss>>i-1&1)e[p1[i]].val++,so++;
else e[p2[i]].val++;
}sum=0;cst=0;
if(so*2!=fl)continue;
while(spfa()){
sum+=flo[t];cst+=flo[t]*dis[t];
int now=t;
while(now!=s){
e[epr[now]].val-=flo[t];
e[epr[now]^1].val+=flo[t];
now=pre[now];
}
}
ans=min(ans,cst);
}
printf("%d",ans);
return 0;
}
T2 排序
这个我考场上想的那个DP,每个节点的数组中都包含了以任何值域范围内的点开头的最小右端点
其实没有必要,只需要存储以当前区间内有的值开头的就可以了
用vector存下来所有的区间,转移的时候直接枚举谁在第一位,如果继续枚举谁是第二位的话就是阶乘复杂度了,显然我们不要这样
那我们发现这个东西和顺序无关,只和状态有关系,于是可以状压
直系儿子就直接继承就好了
关于这样做的复杂度证明,首先我们知道一个节点的区间数量一定是子树内叶子节点内包含的所有m的数量
然后我们假设每一个节点有一个重儿子
那么我们合并顺序中起始节点和终止节点中必然有一个是轻儿子
那么我们的区间数量就是轻儿子数量的两倍,那么复杂度就是\(\mathcal{O(nlogn)}\)
再加上转移的复杂度就好了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define pa pair<int,int>
#define mk(x,y) make_pair(x,y)
#define fi first
#define se second
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*t;
}
const int inf=0x3f3f3f3f;
const int N=1e5+5;
int n,jsq;
vector<int> son[N],ans[N];
bool lf[N];
vector<pair<pa,int>> vec[N],tmp;
int zhi[N];
int so[10],cso,f[1<<10],pre[1<<10],ys[1<<10];
void dfs(int x){
jsq++;
if(lf[x]){
for(int i:son[x])vec[zhi[x]].push_back(mk(mk(i,i),0));
sort(vec[zhi[x]].begin(),vec[zhi[x]].end());
//for(pair<pa,int> i:vec[x])cout<<i.fi.fi<<" ";cout<<endl;
//cout<<"leaf"<<" "<<x<<" "<<vec[x].size()<<endl;
return ;
}
for(int y:son[x])dfs(y);//cout<<vec[y].size()<<endl;
if(son[x].size()==1){
zhi[x]=zhi[son[x][0]];
return ;
}
cso=0;for(int y:son[x])so[++cso]=y;
tmp.clear();int u=(1<<cso-1)-1;
fo(y,1,cso)for(pair<pa,int> q:vec[zhi[so[y]]]){
fo(s,0,u)f[s]=inf;f[0]=q.fi.se;
fo(s,0,u-1){
fo(i,1,cso){
if(i==y)continue;
int pos=i>y?i-2:i-1;
if(!((s>>pos)&1)){
vector<pair<pa,int>>::iterator it=lower_bound(vec[zhi[so[i]]].begin(),vec[zhi[so[i]]].end(),mk(mk(f[s]+1,0),0));
if(it==vec[zhi[so[i]]].end())continue;
//cout<<f[s]<<" "<<so[i]<<" "<<it->fi.fi<<" "<<it->fi.se<<endl;
if(f[s|(1<<pos)]>it->fi.se){
pre[s|(1<<pos)]=s;
f[s|(1<<pos)]=it->fi.se;
}
}
}
}
if(f[u]==inf)continue;
int hs=0,now=u;
fo(i,2,cso)hs=hs*10+(ys[now^pre[now]]>=y-1?ys[now^pre[now]]+1:ys[now^pre[now]]),now=pre[now];
hs=hs*10+y-1;
tmp.push_back(mk(mk(q.fi.fi,f[u]),hs));
}
//if(jsq>=49989)cerr<<x<<" "<<jsq<<" "<<tmp.size()<<endl;
//cerr<<tmp.size()<<endl;
sort(tmp.begin(),tmp.end());
int mx=0;
for(pair<pa,int> i:tmp){
//cout<<i.fi.fi<<" "<<i.fi.se<<endl;
if(vec[zhi[x]].size()&&vec[zhi[x]].rbegin()->fi.fi==i.fi.fi)continue;
while(vec[zhi[x]].size()&&vec[zhi[x]].rbegin()->fi.se>=i.fi.se)vec[zhi[x]].pop_back();
vec[zhi[x]].push_back(i);//cerr<<i.fi.fi<<" "<<vec[x].size()<<endl;
}
//cerr<<endl;
//for(pair<pa,int> i:tmp)cout<<"S"<<" "<<i.fi.fi<<" "<<i.fi.se<<" "<<i.se<<endl;
//cerr<<x<<" "<<vec[x].size()<<endl;
}
void Ans(int x,int l){
//cout<<x<<" "<<l<<endl;
vector<pair<pa,int>>::iterator it=lower_bound(vec[zhi[x]].begin(),vec[zhi[x]].end(),mk(mk(l,0),0));
//cout<<x<<" "<<it->fi.fi<<" "<<it->fi.se<<endl;
if(lf[x])return ans[x].push_back(it->fi.se),void();
if(son[x].size()==1){
ans[x].push_back(son[x][0]);
Ans(son[x][0],l);
return ;
}
int r=it->fi.fi,hs=it->se;
//cout<<"Ans"<<" "<<cso<<" "<<x<<" "<<l<<" "<<hs<<" "<<it->fi.fi<<" "<<it->fi.se<<endl;
fo(i,1,son[x].size()){
int y=son[x][hs%10];
//cout<<x<<" "<<i<<" "<<y<<" "<<r<<endl;
ans[x].push_back(y);
Ans(y,r);hs/=10;
r=lower_bound(vec[zhi[y]].begin(),vec[zhi[y]].end(),mk(mk(r,0),0))->fi.se+1;
}
}
signed main(){
freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
n=read();
fo(i,1,n){
zhi[i]=i;
lf[i]=read()-1;
int x=read();
fo(j,1,x)son[i].push_back(read());
}
fo(i,0,8)ys[1<<i]=i;
//cerr<<"ZZ"<<endl;
dfs(1);
//cerr<<"SB"<<endl;
if(!vec[zhi[1]].size())printf("No");
else {
printf("Yes\n");
Ans(1,1);
fo(i,1,n){
for(int j:ans[i])printf("%d ",j);
printf("\n");
}
}
}
T3 传染
考场上直接暴力\(\mathcal{O(n^2)}\)建图
后来发现有好多的边是可以用前缀优化建图省略的
于是我们用点分治,对于每一个分治中心,我们看子树的点能跨越分治中心走多少距离
排个序,然后后面的距离大的点连向前一个点的虚点就行了在加上可以新接上的点
这样边数就是log了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*t;
}
const int inf=0x3f3f3f3f;
const int N=3e5+5;
int n,rn[N],ans;
struct E{int to,nxt,val;}e[N*2],d[N*100];
int head[N],hea[N*45],rp;
void add_edg(int x,int y,int z){
e[++rp].to=y;e[rp].nxt=head[x];
e[rp].val=z;head[x]=rp;
}
void add_e(int x,int y){
d[++rp].to=y;d[rp].nxt=hea[x];hea[x]=rp;
}
int mx,rt,ms[N],siz[N];
bool vis[N];
void findrt(int x,int f,int sz){
siz[x]=1;ms[x]=0;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==f||vis[y])continue;
findrt(y,x,sz);siz[x]+=siz[y];
ms[x]=max(ms[x],siz[y]);
}
ms[x]=max(ms[x],sz-siz[x]);
if(ms[x]<mx)mx=ms[x],rt=x;
}
int ds[N],cj,rd[N];
ll dis[N];
void get_dis(int x,int f){
ds[++cj]=x;rd[cj]=x;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==f||vis[y])continue;
dis[y]=dis[x]+e[i].val;
get_dis(y,x);
}
}
bool comrd(int x,int y){return rn[x]-dis[x]<rn[y]-dis[y];}
bool comds(int x,int y){return dis[x]<dis[y];}
int seg;
void sol(int x){
cj=0;dis[x]=0;get_dis(x,0);
sort(ds+1,ds+cj+1,comds);
sort(rd+1,rd+cj+1,comrd);
int it=1;
fo(i,1,cj){
int now=rd[i];
if(rn[now]-dis[now]<0)continue;++seg;
if(i>1&&rn[rd[i-1]]-dis[rd[i-1]]>=0)add_e(seg,seg-1);
add_e(now,seg);
while(it<=cj&&dis[ds[it]]<=rn[now]-dis[now]){
add_e(seg,ds[it]);it++;
}
}
}
void dive(int x,int sz){
//cout<<x<<" ";
vis[x]=true;sol(x);
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(vis[y])continue;mx=inf;
if(siz[y]<siz[x])findrt(y,x,siz[y]),dive(rt,siz[y]);
else findrt(y,x,sz-siz[x]),dive(rt,sz-siz[x]);
}
}
int dfn[N*45],low[N*45],cnt,du[N*45];
int sta[N*45],top,bl[N*45],cbl;
void tarjan(int x){
dfn[x]=low[x]=++cnt;sta[++top]=x;
for(int i=hea[x];i;i=d[i].nxt){
int y=d[i].to;
if(!dfn[y])tarjan(y),low[x]=min(low[x],low[y]);
else if(!bl[y])low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x]){
bl[x]=++cbl;
while(sta[top]!=x)bl[sta[top--]]=cbl;
top--;
}
}
signed main(){
freopen("infect.in","r",stdin);
freopen("infect.out","w",stdout);
seg=n=read();
fo(i,1,n)rn[i]=read();
fo(i,1,n-1){
int x=read(),y=read(),z=read();
add_edg(x,y,z);add_edg(y,x,z);
}rp=0;
mx=inf;findrt(1,0,n);dive(rt,n);
fo(i,1,seg)if(!dfn[i])tarjan(i);
cerr<<seg<<" "<<cbl<<" "<<rp<<endl;
fo(x,1,seg){
for(int i=hea[x];i;i=d[i].nxt){
int y=d[i].to;
if(bl[x]==bl[y])continue;
du[bl[y]]++;
//cout<<x<<" "<<y<<" "<<bl[x]<<" "<<bl[y]<<endl;
}
}
fo(i,1,cbl)if(!du[i])ans++;
printf("%d",ans);
}