[noip模拟赛]午餐
https://www.zybuluo.com/ysner/note/1325779
题面
一共有\(n\)个人,林先森知道开始时只有\(1\)号会毒瘤算法。林先森了解到很多人一起吃过
午餐;具体地,有\(m\)条信息,其中第\(j\)条信息描述\(u_j\)和\(v_j\)在\([L_j,R_j]\)区间中的某一天一起吃
了午餐。若此时\(u_j\)或\(v_j\)中的一个人会毒瘤算法,那么两个人都能学会毒瘤算法。特别地,若
一个人在某天和多个人一起吃午餐,那么他在学会毒瘤算法的同时会立即教给别人(同一天的
午餐均视作同时发生)。
林先森知道最后学会了毒瘤算法的同学以及没有学会毒瘤算法的同学,以及一些不确定是
否学会了毒瘤算法的同学。林先森想知道大家具体在哪一天共用了午餐,或者告诉林先森这样
的结果是不可能出现的。
- \(10pts\) \(n,m\leq12,R_i\leq3\)
- \(45pts\) 不存在确定没有学会毒瘤算法的同学
- \(100pts\ n,m\leq2*10^5\)
解析
做难题从部分分开始。
\(45pts\)算法
显然,如果有这条性质,可以推个贪心结论:每个人尽早学会毒瘤算法肯定是最优的。
所以可以设\(f[i]\)表示第\(i\)个人最早学会毒瘤算法的时间。
\(f[v]=\max(f[u],L)\)
然后发现转移等价于最短路。
那么拿\(Dijstra\)跑一跑就行。
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
#define re register
#define il inline
#define pi pair<int,int>
#define mk make_pair
#define fi first
#define se second
#define fp(i,a,b) for(re int i=a;i<=b;++i)
#define fq(i,a,b) for(re int i=a;i>=b;--i)
using namespace std;
const int N=20,M=2e5+100;
int n,m,cho[N],tar[M],top[N],mx,tag=1;
struct dat{int u,v,L,R;}a[M],sta[10][N];
bool vis[M];
int h[M],cnt,dis[M];
struct Edge{int to,nxt,mn,mx;}e[M<<1];
il void add(re int u,re int v,re int mn,re int mx)
{
e[++cnt]=(Edge){v,h[u],mn,mx};h[u]=cnt;
e[++cnt]=(Edge){u,h[v],mn,mx};h[v]=cnt;
}
il ll gi()
{
re ll x=0,t=1;
re char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') t=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
return x*t;
}
il void out()
{
fp(i,1,m) printf("%d\n",cho[i]);
exit(0);
}
il void check()
{
fp(i,1,n) vis[i]=0;vis[1]=1;
fp(i,1,mx)
{
fp(j,1,top[i]) if(vis[sta[i][j].u]||vis[sta[i][j].v]) vis[sta[i][j].u]=vis[sta[i][j].v]=1;
fp(j,1,top[i]) if(vis[sta[i][j].u]||vis[sta[i][j].v]) vis[sta[i][j].u]=vis[sta[i][j].v]=1;
}
re int tag=1;
fp(i,1,n) if((tar[i]==1&&!vis[i])||(tar[i]==-1&&vis[i])) {tag=0;break;}
if(tag) out();
}
il void dfs(re int x)
{
if(x>m) {check();return;}
fp(i,a[x].L,a[x].R)
{
mx=max(mx,a[x].R);
cho[x]=i;
sta[i][++top[i]]=a[x];
dfs(x+1);
--top[i];
}
}
il int min(re int x,re int y){return x<y?x:y;}
il int max(re int x,re int y){return x>y?x:y;}
il void Dijstra()
{
priority_queue<pi,vector<pi>,greater<pi> >Q;
while(!Q.empty()) Q.pop();
fp(i,1,n) vis[i]=0,dis[i]=1e9+7;
dis[1]=0;Q.push(mk(0,1));
while(!Q.empty())
{
re int u=Q.top().se;Q.pop();
vis[u]=1;
for(re int i=h[u];i+1;i=e[i].nxt)
{
re int v=e[i].to,mx=e[i].mx,mn=e[i].mn;
if(dis[v]>max(mn,dis[u])&&dis[u]<=mx)
{
dis[v]=max(mn,dis[u]);
Q.push(mk(dis[v],v));
}
}
while(!Q.empty()&&vis[Q.top().se]) Q.pop();
}
}
il void solve()
{
memset(h,-1,sizeof(h));
fp(i,1,m) add(a[i].u,a[i].v,a[i].L,a[i].R);
Dijstra();
fp(i,1,n) if(tar[i]==1&&dis[i]>1e9) {puts("Impossible");exit(0);}
fp(i,1,m) printf("%d\n",dis[a[i].u]>a[i].R?a[i].L:max(a[i].L,dis[a[i].u]));
exit(0);
}
int main()
{
n=gi();m=gi();
fp(i,1,m)
{
a[i].u=gi(),a[i].v=gi(),a[i].L=gi(),a[i].R=gi();
}
fp(i,1,n) tar[i]=gi(),tag&=(tar[i]>=0);
if(n>12&&tag) solve();//for ex35pts
dfs(1);//for 10pts
puts("Impossible");
return 0;
}
\(100pts\)算法
直接想\(100pts\)是有难度的。
但有\(45pts\)作为铺垫就比较好想了。
如果有确定没学会的人,相当于给他周围的人(与他吃饭的人)的\(f[i]\)的下限\(lim[i]\)。
因为他们吃饭的时间一定在\(L_i\),然后\(f[i]\)就必须大于\(L_i\),即下限为\(L_i+1\)。
(接下来这点自己没想到)
然后这个下限的影响是可以扩散的。
设一个人为\(u\),和他吃饭的某人为\(v\)。
如果\(f[u]>R\),就说明\(v\)不能给\(u\)传授算法,那么他们吃饭时\(v\)不应该会算法。
为保证\(f[v]\)最小,吃饭时间一定为\(L\),则\(lim[v]\geq L+1\)。
这个过程可以用最短路维护。
在此基础上,贪心显然仍成立。
那么接着最短路转移\(f[i]\),过程中保证\(f[i]\geq lim[i]\)即可。
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
#define re register
#define il inline
#define pi pair<int,int>
#define mk make_pair
#define fi first
#define se second
#define fp(i,a,b) for(re int i=a;i<=b;++i)
#define fq(i,a,b) for(re int i=a;i>=b;--i)
using namespace std;
const int M=2e5+100;
int n,m,tar[M],lim[M];
struct dat{int u,v,L,R;}a[M];
bool vis[M];
int h[M],cnt,dis[M];
struct Edge{int to,nxt,mn,mx;}e[M<<1];
il void add(re int u,re int v,re int mn,re int mx)
{
e[++cnt]=(Edge){v,h[u],mn,mx};h[u]=cnt;
e[++cnt]=(Edge){u,h[v],mn,mx};h[v]=cnt;
}
il ll gi()
{
re ll x=0,t=1;
re char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') t=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
return x*t;
}
il int min(re int x,re int y){return x<y?x:y;}
il int max(re int x,re int y){return x>y?x:y;}
il void Dijstra()
{
priority_queue<pi,vector<pi>,greater<pi> >Q;
while(!Q.empty()) Q.pop();
fp(i,1,n) vis[i]=0,dis[i]=1e9+7;
dis[1]=0;Q.push(mk(0,1));
while(!Q.empty())
{
re int u=Q.top().se;Q.pop();
vis[u]=1;
for(re int i=h[u];i+1;i=e[i].nxt)
{
re int v=e[i].to,mx=e[i].mx,mn=e[i].mn,g=max(max(lim[v],mn),dis[u]);
if(dis[v]>g&&g<=mx)
{
dis[v]=g;
Q.push(mk(dis[v],v));
}
}
while(!Q.empty()&&vis[Q.top().se]) Q.pop();
}
}
il void Pre_SPFA()
{
queue<int>Q;
while(!Q.empty()) Q.pop();
fp(i,1,n)
{
tar[i]=gi();
if(tar[i]==-1) lim[i]=1e9+7,vis[i]=1,Q.push(i);
else vis[i]=0;
}
while(!Q.empty())
{
re int u=Q.front();Q.pop();
for(re int i=h[u];i+1;i=e[i].nxt)
{
re int v=e[i].to,mn=e[i].mn,mx=e[i].mx;
if(lim[u]>mx)
if(lim[v]<mn+1)
{
lim[v]=mn+1;
if(!vis[v]) vis[v]=1,Q.push(v);
}
}
vis[u]=0;
}
}
int main()
{
freopen("lunch.in","r",stdin);
freopen("lunch.out","w",stdout);
memset(h,-1,sizeof(h));
n=gi();m=gi();
fp(i,1,m)
{
a[i].u=gi(),a[i].v=gi(),a[i].L=gi(),a[i].R=gi();
add(a[i].u,a[i].v,a[i].L,a[i].R);
}
Pre_SPFA();
Dijstra();
fp(i,1,n)
if(tar[i]==1&&dis[i]>1e9) {puts("Impossible");exit(0);}
fp(i,1,m)
{
if(tar[a[i].u]==-1)
if(dis[a[i].v]<=a[i].L) {puts("Impossible");exit(0);}
if(tar[a[i].v]==-1)
if(dis[a[i].u]<=a[i].L) {puts("Impossible");exit(0);}
}
fp(i,1,m)
{
re int u=a[i].u,v=a[i].v;
if(tar[u]==-1||tar[v]==-1) printf("%d\n",a[i].L);
else printf("%d\n",max(dis[u],dis[v])>a[i].R?a[i].L:max(a[i].L,max(dis[u],dis[v])));
}
fclose(stdin);
fclose(stdout);
return 0;
}