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權值
超級線段樹,不會,咕咕咕……