2023.11.16

A

Little Brother

*2800。

给出两个点 \(A\)\(B\)\(n\) 个圆,此外还有一个未知的圆 \(O\)\(A,B\) 且不与任意圆相交。问 \(O\) 的最小可能半径。

\(1\le n\le 10^5\),点和半径值域 \([-10^5,10^5]\)

答案不超过 \(10^{12}\),要求相对或绝对误差 \(\le 10^{-3}\)


二分一眼假但是放了 \(80\) 分。


B

Rap God

*3400。

一棵树,边权为小写字母。记 \(f(a,b)\) 为沿着 \(a\)\(b\) 的简单路径写下的字符串。

多次询问,给定 \(q,p\),问 \(\sum_{m\not=q}[f(q,p)>f(q,m)]\)

\(n,m\le 2\times 10^4\)\(\mathrm{TL}=7\mathrm{s}\)

GJOJ 上 \(n,m\le 3\times 10^4\)\(\mathrm{TL}=5\mathrm{s}\)


数据很三只 \(\log\)。离线后点分治。


C

[ABC180F] Unbranched

你怎么知道我做过原题还差点没写出来?

\(n\) 个点,\(m\) 条边的满足如下条件的图的数量:

  • 图无自环。
  • 每个点度数 \(\le 2\)
  • 最大连通块大小恰好为 \(k\)

答案模 \(10^9+7\)

\(1\le n,m,k\le 300\)


用均 \(\le k\) 的情况减去均 \(\le k-1\) 的情况就解决了限制 \(3\)

容易观察到连通块一定为链或环。

进一步地,\(n\) 个点的本质不同链个数为 \(\dfrac{n!}{2}\)\(n=1\) 除外),环个数为 \(\dfrac{(n-1)!}{2}\)\(n=2\) 除外)。

考虑 DP,设 \(f_{i,j}\)\(i\) 个点用了 \(j\) 条边的方案数。

如何不重不漏地计算,可以钦定当前连通块一定包含编号最小的那个节点,剩下的随便选即可。

有转移方程

\(\displaystyle f_{i,j}\cdot \binom{n-o-1}{o-1}\cdot\frac{o!}{2}\rightarrow f_{i+o,j+o-1}\),特判 \(o=1\)

\(\displaystyle f_{i,j}\cdot \binom{n-o-1}{o-1}\cdot \frac{(n-1)!}{2}\rightarrow f_{i+o,j+o}\),特判 \(o=2\)

#include<bits/stdc++.h>
#define N 305
#define P 1000000007
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int C[N][N],fac[N];
const int I2=(P+1)>>1;
void init(int n){
	C[0][0]=fac[0]=1;
	for(int i=1;i<=n;i++){
		C[i][0]=C[i][i]=1,fac[i]=1ll*fac[i-1]*i%P;
		for(int j=1;j<i;j++)
			C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
	}
}
int n,m;
int f[N][N];
int solve(int k){
	memset(f,0,sizeof(f));
	f[0][0]=1;
	for(int i=0;i<=n;i++)
		for(int j=0;j<=m;j++)
			for(int o=1;o<=k&&i+o<=n;o++){
				if(j+o-1<=m)
					f[i+o][j+o-1]=(f[i+o][j+o-1]+1ll*f[i][j]*C[n-i-1][o-1]%P*fac[o]%P*(o>1?I2:1))%P;
				if(o>1&&j+o<=m)
					f[i+o][j+o]=(f[i+o][j+o]+1ll*f[i][j]*C[n-i-1][o-1]%P*fac[o-1]%P*(o>2?I2:1))%P;
			}
	return f[n][m];
}
int main(){
	init(300);
	n=read(),m=read();
	int k=read();
	printf("%d\n",(solve(k)-solve(k-1)+P)%P);
	
	return 0;
}

D

Game on Sum (Hard Version)

*2400。

Alice 和 Bob 玩游戏,初始有一个数 \(x=0\)

进行 \(n\) 次操作:

  • Alice 选择实数 \(t\in[0,k]\)
  • Bob 可以令 \(x\leftarrow x+t\)\(x\leftarrow x-t\),且必须至少进行 \(m\) 次加操作。

Alice 欲最大化 \(x\),Bob 欲最小化 \(x\),问两人采用最优策略下 \(x\) 最后的值模 \(10^9+7\)

\(1\le m\le n\le 10^6\)\(1\le k\le 10^9\)


Bob 使用加操作一定不优,也就是加操作被恰好执行 \(m\) 次。

\(f_{i,j}\) 为游戏还剩 \(i\) 轮,Bob 需使用 \(j\) 次加操作时,Alice 能够获得的最大分数(\(n=i,m=j\) 时的答案)。

可以得到 \(f_{i,j}=\min(f_{i-1,j-1}+x,f_{i-1,j}-x)\),当 \(x=\dfrac{f_{i-1,j}-f_{i-1,j-1}}{2}\)\(f_{i,j}\) 取得最大值 \(\dfrac{f_{i-1,j-1}+f_{i-1,j}}{2}\)

边界值 \(f_{i,0}=0,i\in[0,n]\)\(f_{i,i}=i\times k,i\in[0,m]\)

时间复杂度 \(O(nm)\),可以通过 *2100 的 Easy Version。


考虑每个 \(f_{i,i}\) 对最终答案的贡献。从 \(f_{i,i}\)\(f_{n,m}\) 进行了 \(n-i\)\(/2\) 操作。

计算 \(f_{i,i}\) 的贡献次数。可以从 \((x,y)\) 走到 \((x+1,y)\)\((x+1,y+1)\),问不经过 \((k,k),k\in[0,n]\)\((i,i)\) 走到 \((n,m)\) 的方案数。

这相当于从 \((i+1,i)\) 出发走到 \((n,m)\) 的方案数,此时就不用管形如 \((k,k)\) 的点了。

发现方案数就是杨辉三角,即 \(f_{i,i}\) 的贡献次数为 \(\dbinom{n-i-1}{m-i}\)

答案即 \(\displaystyle k\cdot \sum_{i=1}^{m}\frac{i\cdot \binom{n-i-1}{m-i}}{2^{n-i}}\)。特判 \(n=m\) 的情况。

#include<bits/stdc++.h>
#define N 1000010
#define P 1000000007
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int qpow(int k,int b){
	int ret=1;
	while(b){
		if(b&1)ret=1ll*ret*k%P;
		k=1ll*k*k%P,b>>=1;
	}
	return ret;
}
const int I2=(P+1)>>1;
int fac[N],ifac[N],p2[N];
void init(int n){
	fac[0]=ifac[0]=p2[0]=1;
	for(int i=1;i<=n;i++)
		fac[i]=1ll*fac[i-1]*i%P,p2[i]=1ll*p2[i-1]*I2%P;
	ifac[n]=qpow(fac[n],P-2);
	for(int i=n-1;i;i--)
		ifac[i]=1ll*ifac[i+1]*(i+1)%P;
}
int C(int n,int m){
	if(n<0||m<0||n<m)return 0;
	return 1ll*fac[n]*ifac[n-m]%P*ifac[m]%P;
}
int n,m,k;
void solve(){
	n=read(),m=read(),k=read();
	if(n==m){
		int ans=1ll*n*k%P;
		printf("%d\n",ans);
		return;
	}
	int ans=0;
	for(int i=1;i<=m;i++)
		ans=(ans+1ll*i*C(n-i-1,m-i)%P*p2[n-i])%P;
	ans=1ll*k*ans%P;
	printf("%d\n",ans);
}
int main(){
	init(1000000);
	int T=read();
	while(T--)solve();
	
	return 0;
}
posted @ 2023-11-16 15:41  SError  阅读(29)  评论(0)    收藏  举报