CSP模擬3

zhengchenxi發表於2024-09-26

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權值

超級線段樹,不會,咕咕咕……

相關文章