省选模拟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\) 结果做背包都有分

咕咕咕

posted @ 2022-03-08 20:05  Max_QAQ  阅读(52)  评论(0编辑  收藏  举报