雅礼集训2019 Day4
雅礼集训2019 Day4
大爷
第一眼感觉是个线性规划的形式,两棵树一模一样的好像可以贪心
\[\operatorname{maximize} \sum_{i}^{n} x_iw_i\\
\operatorname{s.t.} \left\{
\begin{aligned}
\sum_i x_iA_{i,1} = c_1\\
\cdots \\
\sum_i x_iA_{i,2n} = c_{2n}
\end{aligned}
\right.
\\x\geq 0
\\x\leq 1
\]
暴力单纯形肯定不行,想了一下这东西就继承贪心思路+费用流。s->第一棵树->第二棵树->t即可。
具体建模方式:
#include<bits/stdc++.h>
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
using namespace std;
int read(){
int x=0,pos=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return pos?x:-x;
}
const int N = 601;
int n,m,rt1,rt2;
const int inf = 1e9+7;
struct node{
int v,nex,w,c;
};
int val[N];
int s,t,tot;
int mxf,totc;
namespace dinic{
int head[N*4],cur[N*4];node edge[N*20];int top=1;
int dis[N*4],vis[N*4];
void add(int u,int v,int w,int c){
edge[++top].v=v;edge[top].nex=head[u];head[u]=top;edge[top].w=w;edge[top].c=c;
edge[++top].v=u;edge[top].nex=head[v];head[v]=top;edge[top].w=0;edge[top].c=-c;
}
int bfs(){
queue<int>q;
FOR(i,1,tot) dis[i]=inf,vis[i]=0;
dis[s]=0;q.push(s);
while(!q.empty()){
int now=q.front();q.pop();vis[now]=0;
for(int i=head[now];i;i=edge[i].nex){
int v=edge[i].v;
if(edge[i].w&&dis[v]>dis[now]+edge[i].c){
dis[v]=dis[now]+edge[i].c;
if(!vis[v]){
vis[v]=1;q.push(v);
}
}
}
}
return dis[t]<inf;
}
int dfs(int now,int flow){
int res=flow;
if(now==t||flow==0) return flow;
vis[now]=1;
for(int &i=cur[now];i;i=edge[i].nex){
int v=edge[i].v;
if(!vis[v]&&edge[i].w&&dis[v]==dis[now]+edge[i].c){
int nw=dfs(v,min(res,edge[i].w));
edge[i].w-=nw;edge[i^1].w+=nw;totc+=nw*edge[i].c;res-=nw;
if(!res) return flow;
}
}
vis[now]=0;
return flow-res;
}
void solve(){
while(bfs()){
memcpy(cur,head,sizeof cur);
mxf+=dfs(s,inf);
}
}
}
struct T{
int head[N];node edge[N*2];int top;
int a[N];int w[N];
vector<int>par[N];int tpe,flg;
T(){
FOR(i,1,N-1) a[i]=inf;
top=flg=0;tpe=1;
}
void add(int u,int v){
edge[++top].v=v;
edge[top].nex=head[u];
head[u]=top;
}
void dfs(int now,int pre,int tp){
par[tp].push_back(now);
for(int i=head[now];i;i=edge[i].nex){
int v=edge[i].v;
if(a[v]==inf) a[v]=0;
if(v==pre) continue;
if(a[v]){
w[tp]-=a[v];
if(w[tp]<0) return flg=1,void();
w[++tpe]=a[v];dfs(v,now,tpe);
}else{
dfs(v,now,tp);
}
}
}
void work(){
FOR(i,1,tpe){
int li=par[i].size();int vn=++tot;
dinic::add(s,vn,w[i],0);
for(int j=0;j<li;j++){
int v=par[i][j];
dinic::add(vn,v,1,-val[v]);
}
}
}
void work2(){
FOR(i,1,tpe){
int li=par[i].size();int vn=++tot;
dinic::add(vn,t,w[i],0);
for(int j=0;j<li;j++){
int v=par[i][j];
dinic::add(v,vn,1,0);
}
}
}
}t1,t2;
int main(){
freopen("w.in","r",stdin);
freopen("w.out","w",stdout);
n=read();rt1=read(),rt2=read();
FOR(i,1,n) val[i]=read();
FOR(i,1,n-1){
int u=read(),v=read();
t1.add(u,v);t1.add(v,u);
}
FOR(i,1,n-1){
int u=read(),v=read();
t2.add(u,v);t2.add(v,u);
}
int q0=read();
FOR(i,1,q0){
int x=read(),y=read();
t1.a[x]=min(t1.a[x],y);
}
int q1=read();
FOR(i,1,q1){
int x=read(),y=read();
t2.a[x]=min(t2.a[x],y);
}
s=n+1,t=n+2;tot=n+2;
t1.w[1]=t1.a[rt1];
t1.dfs(rt1,0,1);
if(t1.flg) {
printf("-1");return 0;
}
t1.work();
t2.w[1]=t2.a[rt2];
t2.dfs(rt2,0,1);
if(t2.flg) {
printf("-1");return 0;
}
t2.work2();
dinic::solve();
if(mxf==t1.a[rt1]&&mxf==t2.a[rt2]){
printf("%d",-totc);
}else{
printf("-1");
}
return 0;
}
关于数据,我写了个python把subtask从文件夹里提出来好自动添加试题(没有cdf差评)
import os
import sys
inn=0
outn=0
problemname='n'
def getFileList(dir,mylist):
global inn
global outn
global problemname
for file in os.listdir(dir):
file_path = os.path.join(dir,file) #绝对路径
if os.path.isdir(file_path):
getFileList(file_path,mylist)
else:
if(file_path.endswith('.in')):
inn+=1
os.system ("xcopy %s %s\%s%d.in /s /y /F" % (file_path, mylist, problemname, inn))
else:
if(file_path.endswith('.ans')):
outn+=1
os.system ("xcopy %s %s\%s%d.out /s /y /F" % (file_path, mylist, problemname, outn))
if __name__ == '__main__':
mylist='E:\lemon\雅礼集训\DAY4\data\%s' %(problemname)
getFileList(mylist,mylist)
然而有些问题所以只能半自动。。。(要一直按F)(我不是加了/F吗?没用啊)
熊猫
容斥倒是可以容斥,转化成不能满足某些条件也跟容斥之前差不多...
如果没有大小的限制,直接枚举00的段数,接在一段前面的就是10,接在后面的就是01,剩下的是11,边界情况讨论一下,乘上组合数
那么再枚举一下可以无限制的长度,应该就能做\(len^2\),实现应该会很复杂
也可以构建出自动机,然后就不会了
题解:把枚举改成插板法,讨论略复杂
鸽子
第一次看以为是求任意折线上距离为定值的距离最小值,写了个三分,写完才发现这只是subtask4
正解就是把上面的改一下,把m-1个区间重叠起来,二分答案然后检查有无一个点被覆盖m次即可。具体实现我是二分里先三分再二分,总复杂度\(O(n\log^2n)\)。统计的话覆盖数=左端点-右端点,注意要卡常。
总结:读题重要啊...
#pragma GCC diagnostic error "-std=c++11"
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include<bits/stdc++.h>
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
using namespace std;
const int N = 50001;
inline int read(){
int x=0,pos=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return pos?x:-x;
}
#define db double
struct point{
db x,y;db dis;
point(db x=0,db y=0):x(x),y(y){dis=sqrt((x)*(x)+(y)*(y));}
//db gd(const point &b){
// return sqrt((x-b.x)*(x-b.x)+(y-b.y)*(y-b.y));
// }
point operator -(point b){
return point(x-b.x,y-b.y);
}
point operator +(point b){
return point(x+b.x,y+b.y);
}
point operator *(db b){
return point(x*b,y*b);
}
};
point p[N];
int n,m;
db tot[N];
db di=0;
struct typ{
point s,t;int id;db dis;
typ(point s=point(0,0),point t=point(0,0),int id=0,db dis=0):s(s),t(t),id(id),dis(dis){}
};
typ v[N*2];
db mn[N*2];
int cmp(const typ &a,const typ &b){
if(a.id==b.id){
return a.dis<b.dis;
}else return a.id<b.id;
}
db calc(typ a,typ b,db len){
point si=a.s+(b.s-a.s)*(len/(b.s-a.s).dis);
point ti=a.t+(b.t-a.t)*(len/(b.t-a.t).dis);
return (ti-si).dis;
}
#define pdi pair<db,int>
#define fi first
#define mp make_pair
#define se second
pdi vec[N*4];
int tp=0,top=0,re=0;
void add(db l,db r){
//printf("%f %f\n",l,r);
int ss=((int)(l/di)),tt=((int)(r/di));
if(ss==tt){
l-=di*ss,r-=di*tt;
vec[++tp]=mp(l,1);
vec[++tp]=mp(r,-1);
}else{
re+=tt-ss-1;
//printf("%f %f\n",l,r);
l-=di*ss,r-=di*tt;
//ori++;
vec[++tp]=mp(0,1);
vec[++tp]=mp(r,-1);
vec[++tp]=mp(l,1);
vec[++tp]=mp(di,-1);
//vec[++tp]=mp(di,-1);
//ed++;
}
}
db lmi[N*2],lmx[N*2],rmi[N*2],rmx[N*2];
db lni[N*2],rni[N*2],cnc[N*2];
void getmn(typ a,typ b,int id){
if((a.s-b.s).dis<1e-6) return;
db len=(a.s-b.s).dis;
db l=0,r=len;
while(r-l>1e-6){
db m1=l+(r-l)/3,m2=r-(r-l)/3;
if(calc(a,b,m1)<calc(a,b,m2)){
r=m2;
}else l=m1;
}
mn[id]=l;
lmi[id]=0,lmx[id]=l;
rmi[id]=l,rmx[id]=len;
cnc[id]=calc(a,b,l);
}
inline void update1(db lim){
for(register int i=2;i<=top;++i){
if(cnc[i]>lim) continue;
lmi[i]=lni[i],rmx[i]=rni[i];
}
if(cnc[1]>lim) return;
lmi[1]=lni[1],rmx[1]=rni[1];
}
inline void update2(db lim){
for(register int i=2;i<=top;++i){
if(cnc[i]>lim) continue;
lmx[i]=lni[i],rmi[i]=rni[i];
}
if(cnc[1]>lim) return;
lmx[1]=lni[1],rmi[1]=rni[1];
}
void solve(typ a,typ b,db lim,int id){
// printf("%f %f\n",(a.s-b.s).dis,(a.t-b.t).dis);
//assert(fabs((a.s-b.s).dis-(a.t-b.t).dis)<1e-6);
db low=mn[id];
//db len=(a.s-b.s).dis;
if(calc(a,b,low)>lim) return;
db l=lmi[id],r=lmx[id];
while(r-l>1e-6){
db mid=(l+r)/2.0;
if(calc(a,b,mid)>lim) l=mid;
else r=mid;
}
//add(tot[a.id]+a.dis+l,tot[a.id]+a.dis+low);
lni[id]=l;
db ln=l;
l=rmi[id],r=rmx[id];
while(r-l>1e-6){
db mid=(l+r)/2.0;
if(calc(a,b,mid)>lim) r=mid;
else l=mid;
}
add(tot[a.id]+a.dis+ln,tot[a.id]+a.dis+r);
rni[id]=r;
}
int cmp1(pdi a,pdi b){
if(fabs(a.fi-b.fi)>1e-6) return a.fi<b.fi;
else return a.se<b.se;
}
int check(db mid){
tp=0;re=0;
for(register int i=2;i<=top;++i){
solve(v[i-1],v[i],mid,i);
}
solve(v[top],v[1],mid,1);
int res=re;
sort(vec+1,vec+tp+1,cmp1);
for(int i=1;i<=tp;++i){
res+=vec[i].se;if(res==m) return 1;
//printf("%d\n",res);
}
return 0;
}
int main(){
freopen("n.in","r",stdin);
freopen("n.out","w",stdout);
n=read();m=read();
FOR(i,1,n){
int x=read(),y=read();
p[i]=point(x,y);
//p[i].x=read(),p[i].y=read();
}
FOR(i,1,n){
tot[i]=di;
di+=(p[i%n+1]-p[i]).dis;
}
db w=0;int pos=1;
db dn=di;di=di/m;
if(m>n){
printf("%.10f",di);return 0;
}
FOR(i,1,n){
while(w+(p[pos%n+1]-p[pos]).dis<di) w+=(p[pos%n+1]-p[pos]).dis,pos=pos%n+1;
point nw=p[pos]+(p[pos%n+1]-p[pos])*((di-w)/((p[pos%n+1]-p[pos]).dis));
v[++top]=typ(p[i],nw,i,0);
w-=(p[i%n+1]-p[i]).dis;
}
w=dn,pos=1;
FOR(i,1,n){
while(w-(p[pos%n+1]-p[pos]).dis>di) w-=(p[pos%n+1]-p[pos]).dis,pos=pos%n+1;
point nw=p[pos]+(p[pos%n+1]-p[pos])*((w-di)/((p[pos%n+1]-p[pos]).dis));
v[++top]=typ(nw,p[i],pos,w-di);
w+=(p[i%n+1]-p[i]).dis;
}
//cout<<clock()<<endl;
sort(v+1,v+top+1,cmp);
for(register int i=2;i<=top;++i){
getmn(v[i-1],v[i],i);
}
getmn(v[top],v[1],1);
//cout<<clock()<<endl;
db l=0,r=1e7;
while(fabs(r-l)>1e-6){
db mid=(l+r)/2;
if(check(mid)) r=mid,update1(mid);
else l=mid,update2(mid);
//cout<<clock()<<endl;
}
printf("%.10f",r);
return 0;
}