CSP模拟3

T1 奇观

挺有趣的思路,每个字母相互独立, \(C\)\(F\) ,我们可以把 \(C\) 分成一个两个端点和一个三个端点的路径(以同一个起点开始),而 \(F\) 为了方便统计,我们也可以把它分成两个两个端点和一个三个端点的路径(同样是以同一个端点为起点)。那我们定义 $s_{i} = \sum_{j} [(i,j)双向联通] $ ,\(d_{i}= \sum_{j,k}[(i,j,k)双向联通]=\sum_{j}s_{j}[i,j双向联通]\)

\(C=\sum_{i}s_{i}*d_{i}\) , \(F=\sum_{i}s_{i}^{2}*d_{i}\)

正着遍历肯定会 \(T\) ,比较好知道如果不删点的话,答案为 \(n^{3}*(n-1)^{10}\) ,那我们可以试着看看删掉一个点会对答案造成什么影响,我们先把每个点 \(s_{i}\) 赋成 \((n-1)\) , \(d_{i}\) 赋成 \((n-1)^{2}\),那考虑删点就两种情况,一种是删的点的影响,一种是删掉这个点对其他点的影响,先看后一种,很明显被删的点的 \(s_{i}\) 应减去 \(1\) ,那每个点的 \(d_{i}\) 应该减去 \(2\) (删两个点),那总共每个节点应减去 \((n*2)\) 个点,那只考虑这种肯定有所欠缺,我们既没有考虑这样对删掉点本身不再给另一个点施加影响,而且没考虑删掉点本身的 \(d_{i}\) 不应计算。

现在再考虑第一种情况,对于删掉的点的\(s_{i}\)减去 \(1\)\(d_{i}\) 则要减去 \((n-1)\) ,我们设被删的两个点为\(x,y\),目前就两个问题,一个是已经被删的点再发生变化时,不会再对另一个点造成贡献( \(x,y\) 已经被删, \(y\) 和另一个点再被删)。一个是减去点本身 \(d_{i}\) 也不应该被自身影响而减去值。知道了删点所造成的影响之后就比较好处理了,直接看代码吧。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=3e5+107;
const int mod=998244353;
int n,m;

int read()
{
	int f=1,s=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
	return f*s;
}

int s[N],d[N];
int a[N],b[N];
int vis[N];

signed main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	n=read(),m=read();
	for(int i=1;i<=n;i++) s[i]=n-1,d[i]=s[i]*s[i]%mod;
	for(int i=1;i<=m;i++)
	{
		a[i]=read(),b[i]=read();
		vis[a[i]]++,vis[b[i]]++;
	}
	for(int i=1;i<=m;i++)
	{
		d[a[i]]=(d[a[i]]-(n-1)+vis[b[i]]+mod)%mod;
		d[b[i]]=(d[b[i]]-(n-1)+vis[a[i]]+mod)%mod;
		s[a[i]]--,s[b[i]]--;
	}
	for(int i=1;i<=n;i++)
	{
		d[i]=(d[i]-m*2+(n-1)-s[i]+mod)%mod;
	}
	int ans1=0,ans2=0,ans=1;
	for(int i=1;i<=n;i++)
	{
		(ans1+=d[i]*s[i]%mod)%=mod;
		(ans2+=d[i]*s[i]%mod*s[i])%=mod;
	}
	ans=ans1*ans1%mod*ans2%mod;
	printf("%lld",ans);
}

T2铁路

比较明显可以用并查集来维护点的合并,一个问题就是搜一个点到新开的点时不太好处理,我们可以做一个新开点到原图的映射(好吧,其实也挺好处理的),那这题就没啥了。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

const int N=5e5+107;
int n,m,ans;
int d[N<<1];

int read()
{
	int f=1,s=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
	return f*s;
}

int h[N<<1],to[N<<1],nxt[N<<1],tot;
void add(int x,int y)
{
	to[++tot]=y;
	nxt[tot]=h[x];
	h[x]=tot;
}

int f[N][23],dep[N];
bool vis[N];
void dfs(int u,int fat)
{
	dep[u]=dep[fat]+1;
	f[u][0]=fat;
	for(int i=h[u];i;i=nxt[i])
	{
		int v=to[i];
		if(v==fat) continue;
		if(!vis[v])
		{
			vis[v]=1;
			dfs(v,u);
		}
	}
}

int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=20;i>=0;i--)
	{
		if(dep[f[x][i]]>=dep[y])
		{
			x=f[x][i];
		}
	}
	if(x==y) return x;
	for(int i=20;i>=0;i--)
	{
		if(f[x][i]!=f[y][i])
		{
			x=f[x][i];
			y=f[y][i];
		}
	}
	return f[x][0];
}

int fa[N<<1];
int find(int x)
{
	return x==fa[x]?fa[x]:fa[x]=find(fa[x]);
}

void merge(int x,int y,int i)
{
	
	if(x>n) x=d[x];
	if(y>n) y=d[y];
	int z=find(lca(x,y));
	d[n+i]=z;
	while(x!=z) 
	{
		fa[x]=z;
		x=find(f[x][0]);
		ans--;
	}
	while(y!=z) 
	{
		fa[y]=z;
		y=find(f[y][0]);
		ans--;
	}
}

int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	n=read(),m=read();
	for(int i=1;i<=n;i++) fa[i]=i, d[i]=i;
	for(int i=1;i<=n-1;i++)
	{
		int x=read(),y=read();
		add(x,y); add(y,x);
	}
	dfs(1,1);
	for(int j=1;j<=20;j++)
	{
		for(int i=1;i<=n;i++)
		{
			f[i][j]=f[f[i][j-1]][j-1];
		}
	}
	ans=n;
	for(int i=1;i<=m;i++)
	{
		int a=read(),b=read();
		merge(a,b,i);
		printf("%d\n",ans);
	}
}

T3光纤

计算几何

我们维护一个凸包,然后直接做旋转卡壳,额,剩下没啥了,都是板子。好吧,还是稍微说点,主要是了解一下向量的叉乘(向量积 \(cross\) )和极角排序,向量积它的数值的几何意义比较重要,在二维空间里,它表示是两条边所构成平行四边形的面积,在三维空间里,它表示垂直于两条直线所在平面的法向量。而极角排序可以使用叉积来排序,也可以使用 \(C++\) 自带的 \(atan2\) 排序,它返回的是点 \((x,y)\) 与原点 \((0,0)\) 连线和 \(x\) 轴正方向的夹角弧度( \(atan2()\) 相对于叉积排序精度低,但也够用了,而且它比较快,还方便)。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define int long long

const int N=2e6+107;
int n,m;

int read()
{
	int f=1,s=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
	return f*s;
}

void write(__int128 x)
{
    if(x<0)
        putchar('-'),x=-x;
    if(x>9)
        write(x/10);
    putchar(x%10+'0');
    return;
}

struct Ans
{
	__int128 z,m;
	double val;
	bool operator >=(const Ans &a)const{return val>=a.val;}
	Ans min(Ans a,Ans b){return a.val<b.val?a:b;}
}ans;

struct lmy
{
	int x,y;
	double k;
	bool operator ==(const lmy &a)const{return (a.x==x)&&(a.y==y);}
}st[N],vec[N];
int tp;
bool comp1(lmy a,lmy b)
{
	if(a.y==b.y) return a.x<b.x;
	else return a.y<b.y;
}

int cross(lmy a,lmy b,lmy c)
{
	return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}

//double dis(lmy a,lmy b)
//{
//	return sqrt((a.x-b.x)*(a.x-b.x)*1.0+(a.y-b.y)*(a.y-b.y)*1.0);
//}

Ans dis(lmy a,lmy b,lmy c)
{
	__int128 yz=b.y-c.y,xz=b.x-c.x,n=a.x-b.x,m=b.y-a.y;
	Ans w={yz*yz*n*n+xz*xz*m*m+2*xz*yz*n*m,xz*xz+yz*yz,1.0*(yz*yz*n*n+xz*xz*m*m+2*xz*yz*n*m)/(xz*xz+yz*yz)};
	return w;
}

bool comp(lmy a,lmy b)
{
	if(a.k!=b.k) return a.k<b.k;
	if(a.x==b.x) return a.y<b.y;
	return a.x<b.x;
}

int xx,yy;
signed main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	n=read();
	if(n<=2)
	{
		printf("0/1");
		return 0;
	}
	for(int i=1;i<=n;i++) vec[i]={read(),read()};
	sort(vec+1,vec+n+1,comp1);
	
	n=unique(vec+1,vec+1+n)-(vec+1);
	st[++tp]=vec[1];
	xx=st[tp].x,yy=st[tp].y;
	for(int i=2;i<=n;i++) vec[i].k=atan2(vec[i].y-yy,vec[i].x-xx);
	sort(vec+2,vec+1+n,comp);
	st[++tp]=vec[2];
	for(int i=2;i<=n;i++)
	{
		while(tp>1&&cross(st[tp-1],st[tp],vec[i])<0) tp--;
		st[++tp]=vec[i];
	}
	st[++tp]=st[1];
	
	int l=1,r=1;
	for(l=1;l<tp;l++)
	{
		int last=r;
		while(dis(st[r%tp+1],st[l+1],st[l])>=dis(st[r],st[l+1],st[l]))
		{
			r=r%tp+1;
			if(r==last) return printf("0/1"),0;
		}
		ans=ans.min(ans,dis(st[r],st[l],st[l+1]));
	}
	ans.m*=4;
	__int128 gcd=__gcd(ans.m,ans.z);
	
	write(ans.z/gcd);
	printf("/");
	write(ans.m/gcd);
	return 0;

}

T4权值

超级线段树,不会,咕咕咕……

posted @ 2024-09-26 18:16  zhengchenxi  阅读(13)  评论(0编辑  收藏  举报