选一些写。
Hint:可以点击右下角的目录符号快速跳转到指定位置
4.6
875C
2-SAT,点 (c) 表示这个字符不变,点 (c+m) 表示变为大写。考虑这样两个串:
[s_1=c_1c_2c_3c_4cdots\
s_2=c_1c_2c_3c_5cdots
]
若 (c_4<c_5),则 (c_5) 如果变,(c_4) 必须变;(c_4) 如果不变,(c_5) 必须不变。连 (c_5+m o c_4+m,;c_4 o c_5)。若 (c_4>c_5),则 (c_4) 必须变,(c_5) 必须不变,连 (c_4 o c_4+m,;c_5+m o c_5)。跑一遍 SCC 缩点,选 SCC 编号较小的作为方案。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int N=2e5+10,M=N<<1;
int head[N],ver[M],nxt[M],tot=0;
void add(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
int low[N],dfn[N],cnt=0,num=0,col[N],st[N],top=0;
bool vis[N];
void tarjan(int x)
{
dfn[x]=low[x]=++num;
st[++top]=x;vis[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int y=ver[i];
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
} else if(vis[y]) low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x])
{
cnt++;int y=0;
do{
y=st[top--];
col[y]=cnt;
vis[y]=0;
}while(x!=y);
}
}
vector<int> v[N];
bool check(vector<int> a,vector<int> b)
{
int n=min(a.size(),b.size());
for(int i=0;i<n;i++)if(a[i]!=b[i])return false;
return true;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int k;scanf("%d",&k);
for(int j=1;j<=k;j++)
{
int x;scanf("%d",&x);
v[i].push_back(x);
}
}
for(int i=1;i<n;i++)
{
if(check(v[i],v[i+1]))
{
if(v[i].size()<=v[i+1].size())continue;
else return puts("No"),0;
}
int c=min(v[i].size(),v[i+1].size());
for(int j=0;j<c;j++)
{
if(v[i][j]==v[i+1][j])continue;
if(v[i][j]<v[i+1][j])
{
add(v[i][j],v[i+1][j]);
add(v[i+1][j]+m,v[i][j]+m);
}
else
{
add(v[i][j],v[i][j]+m);
add(v[i+1][j]+m,v[i+1][j]);
}
break;
}
}
for(int i=1;i<=m*2;i++)if(!dfn[i])tarjan(i);
for(int i=1;i<=m;i++)if(col[i]==col[i+m])return puts("No"),0;
puts("Yes");int ans=0;
for(int i=1;i<=m;i++)ans+=(col[i+m]<col[i]);printf("%d
",ans);
for(int i=1;i<=m;i++)if(col[i+m]<col[i])printf("%d ",i);
return 0;
}
4.7
852D
二分答案 (t),网络流 check。Floyd 预处理出所有点对 ((u,v)) 的最短距离 (d(u,v)),若 (d(u,v)le operatorname{mid}),连 (u o v),流量为 (+infty);源点 (S) 连所有 (x_i),流量为 (1);所有点都连向汇点 (T),流量为 (1)。跑一遍最大流,若答案不小于 (k) 则合法。为了避免流混乱,需要把每个点 (u) 拆成两个:(u,;u+V),(u) 向外连边,(u+V) 接受连边。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9') {if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
const int N=1e6+10,inf=0x3f3f3f3f;
int head[N],ver[N],edge[N],nxt[N],tot=1;
void add(int x,int y,int z)
{
ver[++tot]=y;
edge[tot]=z;
nxt[tot]=head[x];
head[x]=tot;
}
int dep[N];bool vis[N];
int S,T;
bool bfs()
{
memset(dep,0x3f,sizeof(dep));
memset(vis,0,sizeof(vis));
dep[S]=0;
queue<int> que;
que.push(S);
while(!que.empty())
{
int x=que.front();que.pop();
vis[x]=0;
for(int i=head[x];i;i=nxt[i])
{
int y=ver[i];
if(dep[x]+1<dep[y]&&edge[i])
{
dep[y]=dep[x]+1;
if(!vis[y])
{
vis[y]=1;
que.push(y);
}
}
}
}
return dep[T]!=inf;
}
int dfs(int x,int limit)
{
if(x==T||!limit)return limit;
int flow=0,rlow=0;
for(int i=head[x];i;i=nxt[i])
{
int y=ver[i];
if(dep[y]==dep[x]+1&&edge[i])
{
if(rlow=dfs(y,min(limit,edge[i])))
{
edge[i]-=rlow;edge[i^1]+=rlow;
flow+=rlow;limit-=rlow;
if(!limit) break;
}
}
}
return flow;
}
int dinic()
{
int ans=0;
while(bfs())ans+=dfs(S,inf);
return ans;
}
int x[N],dis[610][610],v,e,n,k;
bool check(int mid)
{
memset(head,0,sizeof(head));tot=1;
for(int i=1;i<=v;i++) add(i+v,T,1),add(T,i+v,0);
for(int i=1;i<=v;i++)
{
if(x[i]) add(S,i,x[i]),add(i,S,0);
for(int j=1;j<=v;j++)
if(dis[i][j]<=mid)
add(i,j+v,inf),add(j+v,i,0);
}
int ans=dinic();
return ans>=k;
}
int main()
{
v=read(),e=read(),n=read(),k=read();
S=0;T=v*2+2;
for(int i=1;i<=n;i++) x[read()]++;
memset(dis,0x3f,sizeof(dis));
for(int i=1;i<=v;i++)dis[i][i]=0;
for(int i=1;i<=e;i++)
{
int x=read(),y=read(),z=read();
dis[x][y]=dis[y][x]=min(dis[x][y],z);
}
for(int k=1;k<=v;k++)
for(int i=1;i<=v;i++)
for(int j=1;j<=v;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
int l=0,r=1731311,ans=-1;
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid))r=mid-1,ans=mid;
else l=mid+1;
}
printf("%d",ans);
return 0;
}