省选模拟28
A. 我
一开始想到了要连边建图,应该跟网络流有点关系,但没想到要缩点
又看见每条边至少走一次以为是个上下界的网络流
然后模了几个发现假了,就没往网络流上去想了
去看部分分,发现环上的只要空一个出来就能每条边都走
然后就照着这个码了一下部分分,最后也没往回想网络流
那么缩点后如果满了的话就说明至少要让一个点去其他地方
这样强联通分量内的就能都保留了
如果还剩 \(x\) 才能满那就说明可以多从其他地方容纳 \(x\) 个点
又发现就算满了,当他走掉一个后也回空出来一个
根据这个去建图跑网络流,跑出来的就是可以少丢掉的点数,拿所有的加上就是答案
因为不能一上来就流走,所以要拆成入点和出点
Code
#include<bits/stdc++.h>
#define int long long//OVERFLOW !!! MEMORY LIMIT !!!
#define rint signed
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int T,n,m,ans;
int id[500],cnt;
int dfn[70],low[70],siz[70],num[70],clo;
int stk[70],p,bl[70],col;
vector<int>g[110];
char st[110],str[10];
bool vis[70],v[70];
void tarjan(int x){
dfn[x]=low[x]=++clo;vis[stk[++p]=x]=1;
for(auto y:g[x]){
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]){
int k;col++;
do{vis[k=stk[p--]]=0;bl[k]=col;siz[col]++;}while(k!=x);
}
}
namespace netflow{
int head[200],HD[200],ver[10000],edge[10000],to[10000],tot=1;
int dis[200],q[200],h,t,S,T;
inline void add(int x,int y,int z){
ver[++tot]=y;edge[tot]=z;to[tot]=head[x];head[x]=tot;
ver[++tot]=x;edge[tot]=0;to[tot]=head[y];head[y]=tot;
}
inline bool bfs(){
for(int i=1;i<=T;i++) dis[i]=inf;memcpy(head,HD,sizeof(HD));
dis[S]=0;q[h=t=1]=S;
while(h<=t){
int x=q[h++];
for(int i=head[x],y;i;i=to[i]) if(edge[i]) if(dis[y=ver[i]]>dis[x]+1) dis[q[++t]=y]=dis[x]+1;
if(x==T) return true;
}
return false;
}
int dfs(int x,int in){
if(x==T) return in;
int res=in,go;
for(int i=head[x];i;head[x]=i=to[i]) if(edge[i]){
int y=ver[i];
if(dis[y]!=dis[x]+1) continue;
go=dfs(y,min(res,edge[i]));
if(go) res-=go,edge[i]-=go,edge[i^1]+=go;
else dis[y]=inf;
if(!res) break;
}
return in-res;
}
inline int dinic(){
memcpy(HD,head,sizeof(head));
int res=0;while(bfs()) res+=dfs(S,inf);
return res;
}
inline void clear(){memset(head,0,sizeof(head));tot=1;}
}using netflow::add;
inline void solve(){
scanf("%s",st+1);n=strlen(st+1);m=read();ans=0;
for(int i=1;i<=cnt;i++) g[i].clear();clo=col=0;
memset(v,0,sizeof(v));memset(bl,0,sizeof(bl));
memset(vis,0,sizeof(vis));memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));memset(num,0,sizeof(num));
memset(siz,0,sizeof(siz));
netflow::clear();
for(int i=1;i<=n;i++) v[id[st[i]]]=1;
for(int i=1;i<=cnt;i++) if(v[i]) ans++;
for(int i=1;i<=m;i++){scanf("%s",str+1);g[id[str[1]]].emplace_back(id[str[2]]);}
for(int i=1;i<=cnt;i++) if(!dfn[i]) tarjan(i);
for(int i=1;i<=cnt;i++){
num[bl[i]]+=v[i];
for(auto y:g[i]) if(bl[i]!=bl[y]) add(bl[i]+cnt,bl[y],inf);
}
for(int i=1;i<=col;i++){
if(siz[i]==1&&num[i]==1&&!netflow::head[i+cnt]) continue;
if(siz[i]==num[i]) add(netflow::S,i+cnt,1),ans--;
add(i,netflow::T,siz[i]-num[i]+(siz[i]==num[i]));
add(i,i+cnt,inf);
}
ans+=netflow::dinic();
printf("%lld\n",ans);
}
signed main(){
#ifdef LOCAL
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
for(int i='a';i<='z';i++) id[i]=++cnt;
for(int i='A';i<='Z';i++) id[i]=++cnt;
for(int i='0';i<='9';i++) id[i]=++cnt;
netflow::S=cnt*2+1;netflow::T=cnt*2+2;
T=read();while(T--) solve();
return 0;
}
B. 想不出
偏 \(30\) 度是为了保证没有两个点共线
所以可以转化成向上和向右做射线
然后根据欧拉公式每个联通块内的有限面数为 \(\text{交点数(边数)}-\text{射线数(点数)}+1\)
然后让交点尽可能的多,发现存在一条分界线,以上的全部向上,以下的全部向右
分界线上的一条射线,他的方向跟他右下方和左上方的点数有关
哪一边多就指向哪一边,判断完了方向再算有限面数就行
Code
#include<bits/stdc++.h>
//#define int long long//OVERFLOW !!! MEMORY LIMIT !!!
#define rint signed
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,now,ans;
double T=10;
struct data{
int x,y;
double k1,k2,b1,b2;
inline bool operator<(const data &b)const{return (x==b.x)?y<b.y:x<b.x;}
}L[2010];
int vis[2010];
bool dir[2010];
inline bool jud(int x,int y){
double pos=1.0*(L[y].b1-L[x].b2)/(L[x].k2-L[y].k1);
return (1.0*L[x].x<=pos&&pos<=1.0*L[y].x);
}
inline int calc(){
int res=0;
for(int i=1;i<=n;i++) vis[i]=0;
for(int i=1,t;i<=n;i++) if(dir[i]==1){
t=-1;
for(int j=i+1;j<=n;j++) if(dir[j]==0){
double x=1.0*(L[j].b1-L[i].b2)/(L[i].k2-L[j].k1);
if(1.0*L[i].x<=x&&x<=1.0*L[j].x){if(!vis[j]) vis[j]=1;else t++;}
}
res+=max(t,0);
}
return res;
}
signed main(){
#ifdef LOCAL
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
freopen("surface.in","r",stdin);
freopen("surface.out","w",stdout);
n=read();
for(int i=1;i<=n;i++) L[i].x=read(),L[i].y=read();sort(L+1,L+1+n);
for(int i=1;i<=n;i++){
L[i].k1=1.0*sqrt(3.0);L[i].k2=-L[i].k1;
L[i].b1=1.0*L[i].y-1.0*L[i].k1*L[i].x;
L[i].b2=1.0*L[i].y-1.0*L[i].k2*L[i].x;
}
for(int i=1,c0,c1;i<=n;i++){
c0=c1=0;
for(int j=1;j<i;j++) if(jud(j,i)) c0++;
for(int j=n;j>i;j--) if(jud(i,j)) c1++;
dir[i]=(c0<c1);
}
printf("%d\n",calc());
return 0;
}
C. 题目名称
这数据离谱,以为都到了 \(2e8\) 结果做背包都有分
咕咕咕