2023.1.31 日寄

2023.1.31 日寄

一言

\(~~~~\) 我们飞得越高,在那些不能飞的人眼中的形象就越是渺小。——尼采

今日(理论)复习内容:数数

「ARC117C」Tricolor Pyramid

题解

\(~~~~\) 这种东西一般都是构造一些东西来量化每种颜色,然后可以方便递推。

\(~~~~\) 这题简单构造一个 \(\bmod 3\) 意义下的东西,那么你会发现上一层放的数是 \(-(a_1+a_2)\) ,当然还是 \(\bmod 3\) 意义下的。那以此类推,你会发现每个底层的贡献是类似于杨辉三角的方式算的,那直接用Lucas定理算组合数就好了。

\(~~~~\) 然后你会发现差距为奇数层时的贡献取负数,否则取正数。考虑进去算出最上一层的答案就好了。

代码
查看代码
#include <bits/stdc++.h>
using namespace std;
template<typename T>void read(T &x)
{
    T f=1;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    x*=f;
}
char ToC[5];
int ToN[10000];
const int MOD=3;
inline int Add(int a,int b){return (a+b)%MOD;}
inline int Dec(int a,int b){return (a-b+MOD)%MOD;}
inline int Mul(int a,int b){return 1ll*a*b%MOD;}
inline int qpow(int a,int b)
{
	int ret=1;
	while(b)
	{
		if(b&1) ret=ret*a%MOD;
		b>>=1;a=Mul(a,a);
	}
	return ret;
}
int C(int n,int r)
{
	if(r>n) return 0;
	if(n==0) return 1;
	if(n==1) return 1;
	if(n==2&&(r==0||r==2)) return 1;
	if(n==2&&r==1) return 2;
	if(n==3&&(r==0||r==3)) return 1;
	return 0;
}
char S[400005];
int Lucas(int a,int b){if(a<3&&b<3) return C(a,b);return Mul(C(a%3,b%3),Lucas(a/3,b/3));}
int main() {
	ToC[0]='B'; ToC[1]='W'; ToC[2]='R';
	ToN['B']=0; ToN['W']=1; ToN['R']=2;
	int n;read(n);
	scanf("%s",S+1);
	int Ans=0;
	for(int i=1;i<=n;i++) Ans=Add(Ans,Mul(Lucas(n-1,i-1),ToN[S[i]]));
	putchar(ToC[n&1?Ans:Dec(0,Ans)]);
	return 0;
}
/*
西风吹老洞庭波,一夜湘君白发多。
醉后不知天在水,满船清梦压星河。
*/

「AGC035D」Add and Remove

题解

\(~~~~\) 最后剩下的两个数肯定基础是有原序列最左和最右的两个数的,那我们只需最小化剩下的 \(16\) 个数带来的增量。

\(~~~~\) 我们考虑倒序,也就是把每个数填回去,然后考虑这个数对两侧的量的贡献。

\(~~~~\) 那么因此我们需要加入 \(x,y\) 表示区间 \([l,r]\) 内的数,当前填会对左侧造成 \(x\) 倍贡献,右侧造成 \(y\) 倍贡献(当然最后不一定是加到最左或最右的数上)

\(~~~~\) 那么对于一个 \((l,r,x,y)\) ,我们枚举区间内最后删的数的下标 \(t\),那它的增量贡献 \(a_t\times (x+y)\) ,同时会分裂成两个区间:\((l,t,x,x+y)\)\((t,r,x+y,y)\) 这两个区间递归下去算就好了。最后总的采用记忆化搜索,可以注意到 \(x,y\leq 800\) ,所以状态数是 \(16^2\times 800^2\) 并且还有很多不合法的,必然可以过。

代码
查看代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
const ll INF=2e18;
ll arr[20];
ll dp[20][20][800][800];
ll Solve(int l,int r,ll x,ll y)
{
	if(l+1>=r) return 0ll;
	if(dp[l][r][x][y]) return dp[l][r][x][y];
	ll ret=INF;
	for(ll i=l+1;i<r;i++) ret=min(Solve(l,i,x,x+y)+Solve(i,r,x+y,y)+((ll)x+(ll)y)*arr[i],ret);
	return dp[l][r][x][y]=ret;
}
int main() {
//	memset(dp,-1,sizeof(dp));
	int n;
	scanf("%d",&n);for(ll i=1;i<=n;i++) scanf("%lld",&arr[i]);
	printf("%lld",Solve(1,n,1ll,1ll)+arr[1]+arr[n]);
	return 0;
}

「HAOI2018」染色

题解

\(~~~~\) 又被这一类题目杀爆了。

\(~~~~\) 还是用钦定的方法,那这里我们钦定恰好有 \(k\) 种颜色出现 \(S\),那么其他的乱放:

\[\binom{m}{k}\times \binom{n}{sk}\times \frac{(s\times k)!}{(s!)^k}\times (m-k)^{n-sk} \]

\(~~~~\) 把这个东西记作 \(F_k\) 然而我们推出来的到底是个啥呢?你发现它既不是“恰好”,也不是“至少”。因为它会算重很多东西,比如说 \(1,2,3\) 被钦定,然后 \(4\) 被乱放到恰好 \(k\) 个和 \(1,2,4\) 被钦定,然后 \(3\) 被乱放到恰好 \(k\) 个。这两者就会重复算。

\(~~~~\) 但细思就会发现这是所有恰好\(i\) 个的方案乘上 \(\binom{i}{k}\) 的贡献。我们不妨把恰好 \(i\) 个的方案数记作 \(G_i\),那就有:

\[F_i=\sum_{j=i}^n \binom{j}{i} G_j \]

\(~~~~\) 由二项式反演:

\[G_i=\sum_{j=i}^n (-1)^{j-i}\binom{j}{i}F_i \]

\(~~~~\) 所以我们就有了 \(\mathcal{O(n^2)}\) 求出 \(G\) 的方法。直接来会 T,但这种东西一脸可以卷积的样子:

\[G_i=\sum_{j=i}^n (-1)^{j-i} \frac{j!}{i!(j-i)!}F_j\\G_{i}\times i!=\sum_{j=i}^n \frac{(-1)^{j-i}}{(j-i)!}\times F_j\times j! \]

\(~~~~\) 那确实可以卷积了。

代码
查看代码
#include <bits/stdc++.h>
using namespace std;
template<typename T>void read(T &x)
{
    T f=1;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    x*=f;
}
const int MOD=1004535809;
const int g=3;
inline int Add(int a,int b){return (a+b)%MOD;}
inline int Dec(int a,int b){return (a-b+MOD)%MOD;}
inline int Mul(int a,int b){return 1ll*a*b%MOD;}
inline int qpow(int a,int b)
{
	int ret=1;
	while(b)
	{
		if(b&1) ret=Mul(ret,a);
		b>>=1;a=Mul(a,a); 
	}
	return ret;
}
const int gi=qpow(g,MOD-2);
int N,Lg,n,m,s,Val[100005];
int Fac[10000005],Inv[10000005],Pow[10000005];
int F[400005],G[400005],To[400005];
inline int C(int NN,int r){return NN<r?0:Mul(Fac[NN],Mul(Inv[r],Inv[NN-r]));}
inline int f(int k){return n<s*k?0:Mul(Mul(C(m,k),C(n,s*k)),Mul(Mul(Fac[s*k],Pow[k]),qpow(m-k,n-s*k)));}
void NTT(int *S,int op)
{
	for(int i=0;i<N;i++) if(i<To[i]) swap(S[i],S[To[i]]);
	for(int i=1;i<N;i<<=1)
	{
		int W=qpow((op==1)?g:gi,(MOD-1)/(i<<1));
		for(int j=0;j<N;j+=i<<1)
		{
			int w=1;
			for(int k=0;k<i;k++,w=Mul(w,W))
			{
				int x=S[j+k],y=Mul(S[i+j+k],w);
				S[j+k]=Add(x,y); S[i+j+k]=Dec(x,y);
			}
		} 
	}
	if(op==-1)
	{
		int inv=qpow(N,MOD-2);
		for(int i=0;i<N;i++) S[i]=Mul(S[i],inv);
	}
}
int main() {
	read(n);read(m);read(s);
	int Up=max(n,m);
	Fac[0]=1;						for(int i=1;i<=Up;i++) Fac[i]=Mul(Fac[i-1],i);
	Inv[Up]=qpow(Fac[Up],MOD-2);	for(int i=Up-1;~i;i--) Inv[i]=Mul(Inv[i+1],i+1);
	for(int i=0;i<=m;i++) read(Val[i]); 
	Up=min(n/s,m); Pow[0]=1;
	for(int i=1;i<=Up;i++) Pow[i]=Mul(Pow[i-1],Inv[s]);
	for(int i=0;i<=Up;i++)
	{
		F[i]=Mul(f(Up-i),Fac[Up-i]);
		G[i]=(i&1)?MOD-Inv[i]:Inv[i];
	}
	for(N=1,Lg=0;N<Up+Up+2;N<<=1,Lg++);
	for(int i=0;i<N;i++) To[i]=(To[i>>1]>>1)|((i&1)?N>>1:0);
	NTT(F,1); NTT(G,1);
	for(int i=0;i<N;i++) F[i]=Mul(F[i],G[i]);
	NTT(F,-1);
	reverse(F,F+Up+1);
	int Ans=0;
	for(int i=0;i<=Up;i++) Ans=Add(Ans,Mul(Mul(F[i],Inv[i]),Val[i]));
	printf("%d",Ans);
	return 0;
}
/*
西风吹老洞庭波,一夜湘君白发多。
醉后不知天在水,满船清梦压星河。
*/

「AGC032E」Modulo Pairing

题解

\(~~~~\) 不加证明地给出结论:存在分界点使得前后各自匹配满足前面和后面的段匹配全部各自首尾相配,且后面的段匹配均 \(\geq m\) 。并且这个分界点越靠左越好。

\(~~~~\) 具体证明大概就是枚举四种情况调整,可以参看 这篇题解

\(~~~~\) 所以我们可以二分求最适的分界点。

代码
查看代码
#include <bits/stdc++.h>
using namespace std;
template<typename T>void read(T &x)
{
    T f=1;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    x*=f;
}
int n,m;
int Ans,arr[200005];
bool check(int x)
{
	for(int i=x+1,j=n;i<=j;i++,j--) if(arr[i]+arr[j]<m) return false;
	return true;
}
int main() {
	read(n);read(m);
	for(int i=1;i<=2*n;i++) read(arr[i]);
	sort(arr+1,arr+1+2*n); n<<=1;
	int l=0,r=n>>1,mid,Pos=0;
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(check(mid<<1)) r=mid-1,Pos=mid<<1;
		else l=mid+1;
	}
	for(int i=1,j=Pos;i<=j;i++,j--) Ans=max(Ans,arr[i]+arr[j]);
	for(int i=Pos+1,j=n;i<=j;i++,j--) Ans=max(Ans,arr[i]+arr[j]-m);
	printf("%d",Ans);
	return 0;
}
/*
西风吹老洞庭波,一夜湘君白发多。
醉后不知天在水,满船清梦压星河。
*/

「AGC036F」Square Constraints

题解

\(~~~~\) 今天非常困,不写了先。

代码
查看代码
#include <bits/stdc++.h>
using namespace std;
template<typename T>void read(T &x)
{
    T f=1;x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    x*=f;
}
int n,MOD;
inline int Add(int a,int b){return (a+b)%MOD;}
inline int Dec(int a,int b){return (a-b+MOD)%MOD;}
inline int Mul(int a,int b){return 1ll*a*b%MOD;}
inline int qpow(int a,int b)
{
	int ret=1;
	while(b)
	{
		if(b&1) ret=Mul(ret,a);
		b>>=1;a=Mul(a,a);
	}
	return ret;
}
int dp[505][505];
pair<int,int>P[505];
int Calc(int x)
{
	memset(dp,0,sizeof(dp));
	dp[0][0]=1;
	int Lim=0,r=0;
	for(int i=1;i<=2*n;i++)
	{
		if(P[i].second==0)
		{
			for(int j=0;j<=Lim;j++) dp[i][j]=Add(dp[i][j],Mul(dp[i-1][j],(Dec(P[i].first,Add(r,j-1)))));
			r++;
		}
		else
		{
			for(int j=0;j<=Lim;j++)
			{
				dp[i][j]=Add(dp[i][j],Mul(dp[i-1][j],P[i].second+1-n-x-Lim+j));
				dp[i][j+1]=Add(dp[i][j+1],Mul(dp[i-1][j],P[i].first-r-j+1)); 
			}
			Lim++;
		}
	}
	return dp[2*n][x];
}
int main() {
	read(n);read(MOD);
	for(int i=0;i<n;i++)
	{
		P[i+1].first=ceil(sqrt(n*n-i*i))-1;
		P[i+1].second=min(2*n-1,(int)floor(sqrt(4*n*n-i*i)));
	}
	for(int i=n;i<2*n;i++)
	{
		P[i+1].first=min((int)floor(sqrt(4*n*n-i*i)),2*n-1);
		P[i+1].second=0;
	}
	sort(P+1,P+1+2*n);
	int Ans=0;
	for(int i=0;i<=n;i++)
	{
		if(i&1) Ans=Dec(Ans,Calc(i));
		else Ans=Add(Ans,Calc(i));
	}
	printf("%d",Ans); 
	return 0;
}
/*
西风吹老洞庭波,一夜湘君白发多。
醉后不知天在水,满船清梦压星河。
*/

鲜花

\(~~~~\) 究竟是我已经被“驯化”抑或是太阳过于张扬,让本就渺小的尘埃更自卑。

\(~~~~\) 然后说的今天写的想法果然已经忘得彻彻底底了。没办法,烂人睡一觉什么都会忘,也不知道这究竟算好还是坏。

posted @ 2023-01-31 19:21  Azazеl  阅读(31)  评论(0编辑  收藏  举报