【SMOJ模拟】2018.12.26 反正都是原题系列2

版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/85301421

【前言】
原题是 COCI \text{COCI} 的,然而懒得找。

T1

【题目】
一个 n × m n\times m 的二维矩阵,第 i i 行第 j j 列的价值是 w i , j w_{i,j} ,初始起点在第 A A 行第 B B 列的格子,要走 K K 步,最终回到起点,每一步可以走四相邻的格子。每进入一个格子可以得到该格子的价值,可以重复进入相同格子,而且重复进入可以得到当前格子。求能获得最大价值。 n , m 100 , k , w i , j 1 0 9 n,m\leq 100,k,w_{i,j}\leq 10^9 ,且 k k 是偶数

【解题思路】
我们可以 YY \text{YY} 一下答案的组成,那么一定是沿着一条路径走,然后反复横跳,再沿着路径走回去。
因为所有路径最长只有 n × m n\times m ,于是我们可以暴力迭代前 n × m n\times m 步,剩下的步数直接计算即可。

【参考代码】

#pragma GCC optimize("O3")
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=105,MX=10000;
int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
int n,m,bx,by,K,now,las;
int a[N][N];
ll ans,f[2][N][N];

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

void init()
{
	now=0;las=1;
	n=read();m=read();bx=read();by=read();K=read();
	for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) a[i][j]=read();
	memset(f,0xc0,sizeof(f));f[now][bx][by]=0;
}

void walk()
{
	now^=1;las^=1;
	for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) for(int d=0;d<4;++d)
		f[now][i][j]=max(f[now][i][j],f[las][i+dx[d]][j+dy[d]]+a[i][j]);
}

void solve()
{
	if(K<=MX) 
	{
		for(int i=1;i<=K;++i) walk();
		printf("%lld\n",f[now][bx][by]);
	}
	else
	{
		for(int i=1;i<=MX/2;++i) walk();
		for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) for(int d=0;d<4;++d)
			ans=max(ans,f[now][i][j]*2-a[i][j]+(ll)(K-MX)/2*(a[i][j]+a[i+dx[d]][j+dy[d]]));
		printf("%lld\n",ans);
	}
}

int main()
{
	freopen("2738.in","r",stdin);
	freopen("2738.out","w",stdout);

	init();solve();
	return 0;
}

T2

【题目】
有一个 n × m n\times m 的网格,在 k k 秒内每秒会有一场灾难,用 ( x , y , r ) (x,y,r) 来表示,意为 ( x i , y i ) , ( x i x ) 2 + ( y i y ) 2 r 2 \forall (x_i,y_i), (x_i-x)^2+(y_i-y)^2\leq r^2 ,将 ( x i , y i ) (x_i,y_i) 上的数置零,否则加一。初始每个格子上的数字为 1 1 ,问 k k 秒后所有数之和。 n , m 1 0 5 , k 100 n,m\leq 10^5,k\leq 100

【解题思路】
我们可以对每一条横线进行考虑,那么我们要考虑的就是一个线段与圆交的问题,这个可以通过简单计算完成。我们现在得到每条斜线上的若干个交点,用一个 set \text{set} 来维护计算这条斜线上最终所有数字的和即可。

【参考代码】

#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
const int N=105;
int lx,ly,n;
ll ans;
vector<pii>p;
set<int>st;

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}
ll sqr(ll x){return x*x;}

struct data
{
	ll x,y,r;
	void in(){x=read();y=read();r=read();r=sqr(r);}
}a[N];

ll calc()
{
	ll res=0;int las=1;
	sort(p.begin(),p.end());st.clear();st.insert(0);
	//for(int i=0;i<p.size();++i) printf("%d %d\n",p[i].fi,p[i].se); puts("");
	for(int i=0;i<(int)p.size() && las<=ly;++i)
	{
		if(p[i].fi>las) res+=(p[i].fi-las)*(*st.rbegin()),las=p[i].fi;
		if(p[i].se>0) st.insert(p[i].se);
		else st.erase(-p[i].se);
	}
	return res;
}

int main()
{
	freopen("2741.in","r",stdin);
	freopen("2741.out","w",stdout);

	lx=read();ly=read();n=read();ans=(ll)lx*ly*n;
	for(int i=1;i<=n;++i) a[i].in();
	for(int i=1;i<=lx;++i)
	{
		p.clear();
		for(int j=1;j<=n;++j) if(a[j].r>=sqr(i-a[j].x))
		{
			ll t=sqrt(a[j].r-sqr(i-a[j].x));
			p.pb(mkp(a[j].y-t,j));p.pb(mkp(a[j].y+t+1,-j));
		}
		p.pb(mkp(ly+1,0));
		ans-=calc();
	}
	printf("%lld\n",ans);
	return 0;
}

T3

【题目】
n n 个人,每个人可以挑战人也可以被挑战,现在已知每个人是否挑战人和是否被挑战成功。挑战是有时间顺序的。当一个人被挑战成功后,接下来所有对他的挑战都会失败,同时他将不再会挑战别人。一个人最多挑战一次人,求满足要求的方案数。 n 2500 n\leq 2500

【解题思路】
n n 个人,每个人可以挑战人也可以被挑战,现在已知每个人是否挑战人和是否被挑战成功。挑战是有时间顺序的。当一个人被挑战成功后,接下来所有对他的挑战都会失败,同时他将不再会挑战别人。一个人最多挑战一次人,求满足要求的方案数。 n 2500 n\leq 2500

这道题初看十分神奇,因为需要满足的顺序限制很多,而且“挑战成功了还可以被继续挑战”这种限制更是难办,然后好像就做不了了。
但细细分析一下,我们可能可以得到一些可行的转化。
我们可以将这些挑战看作一个个序列,每一个序列均由 YN \text{YN} 开头,后面跟着若干个 YY \text{YY} ,最后由 NY \text{NY} YY \text{YY} YN \text{YN} 结尾。
为什么可以这样考虑?其实需要抓住一个关键,结尾的人若是 NY \text{NY} ,显然是可以的;若是 YY \text{YY} YN \text{YN} ,那它不论挑战什么都是一样的,因为都失败了。
这里似乎没有考虑 NN \text{NN} ,然而确实不用考虑。

所以我们的思路大概就出来了:给每个 YN \text{YN} 序列分配若干个 YY \text{YY} ,然后补上 NY \text{NY} ,最后将多出来的 NY \text{NY} 任意分配。

以下我们设 c n t 1 cnt_1 表示 YN \text{YN} 的个数, c n t 3 cnt_3 表示 YY \text{YY} 的个数。(为什么没有 c n t 2 cnt_2 ?因为它是 NY \text{NY} 的个数)

我们首先考虑知道了每个 YN \text{YN} 序列分配了多少个 YY \text{YY} 序列时我们怎么统计贡献?那么实际上就是一个经典的排列问题,设个数分别为 k i k_i 个,答案就是 c n t 3 ! k i ! \frac{cnt_3!}{\prod k_i!}

我们设 f i , j f_{i,j} 表示前 i i YN \text{YN} ,一共分配了 j j YY \text{YY} 的方案数。则:
f i , j = k = 0 j f i 1 , j k ( c n t 3 ( j k ) ) ! ( c n t 3 j ) ! ( k + 1 ) ! f_{i,j}=\sum_{k=0}^j f_{i-1,j-k}\cdot \frac{(cnt_3-(j-k))!} {(cnt_3-j)!\cdot (k+1)!}
其中 ( c n t 3 ( j k ) ) ! ( c n t 3 j ) ! = P c n t 3 ( j k ) k \frac{(cnt_3-(j-k))!} {(cnt_3-j)!}=P^k_{cnt_3-(j-k)} ,具体意义是我们分配了 k k YY \text{YY} ,然后还要除以一个顺序, + 1 +1 是因为还有开头的 YN \text{YN}
那么观察到阶乘项与 f f 的第二维长得十分像,我们进行移项,可以得到:
f i , j ( c n t 3 j ) ! = k = 0 j f i 1 , j k ( c n t 3 ( j k ) ) ! ( k + 1 ) ! f_{i,j}\cdot (cnt_3-j)!=\sum_{k=0}^j f_{i-1,j-k}\cdot \frac{(cnt_3-(j-k))!} {(k+1)!}
这样令 g i , j = f i , j ( c n t 3 j ) ! g_{i,j}=f_{i,j}\cdot (cnt_3-j)! h i = 1 ( i + 1 ) ! h_i=\frac 1 {(i+1)!} ,我们可以得到
g i , j = k = 0 j g i 1 , j k h i g_{i,j}=\sum_{k=0}^j g_{i-1,j-k} \cdot h_i
这显然是个卷积的形式,于是:
G ( x ) × H ( x ) = G ( x ) G(x)\times H(x)=G&#x27;(x)
这里 G ( x ) G(x) 开始为 1 1 ,那么我们快速幂暴力卷积就可以做到 O ( n 2 log n ) O(n^2\log n) 了,当然如果你用 NTT \text{NTT} 优化一下就是 O ( n log 2 n ) O(n\log^2n) 了。

不过我们还能继续优化,而且写起来更简单。
观察到我们要求的实际上是 H c n t 3 ( x ) H^{cnt_3}(x) ,而这个形式十分优美:
H ( x ) = i 0 x i ( i + 1 ) ! H(x)=\sum_{i\geq 0} \frac {x^i} {(i+1)!}
约定 0 ! = 1 0!=1 根据泰勒展开 e x = i 0 x i i ! e^x=\sum_{i\geq 0} \frac {x^i} {i!} ,那么我们实际上就是要将 e x e^x 去掉常数项再左移一项,于是:
H ( x ) = e x 1 x H(x)=\frac {e^x-1} x
将它展开:
H ( x ) = 1 x i = 0 n ( 1 ) n i e i x ( n i ) H(x)=\frac 1 x\sum_{i=0}^n(-1)^{n-i}e^{ix}{n\choose i}
由于右边有一个 1 x \frac 1 x ,实际上每有一个相当于要求的幂次 + 1 +1
于是我们将 1 x \frac 1 x 去掉后,我们现在要求的是这个东西的 c n t 3 cnt_3 次方的系数
H ( x ) = i = 0 n ( 1 ) n i e i x ( n i ) H(x)=\sum_{i=0}^n(-1)^{n-i}e^{ix}{n\choose i}
于是我们再展开 e i x e^{ix} 就可以得到答案了。
这样复杂度就是 O ( n log n ) O(n\log n) 的了(快速幂的 log \log

【参考代码1】( O ( n 2 log n ) O(n^2\log n) )

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=2505,mod=1e9+7;
int n,cnt[4];//1=YN,2=NY,3=YY
char s1[N],s2[N];
ll ans,fac[N],ifac[N];

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

struct poly
{
	ll n,a[N<<1];
	void clear(){for(int i=0;i<N<<1;++i)a[i]=0;n=0;}
	poly operator *(const poly &x)const
	{
		poly res;memset(res.a,0,sizeof(res.a));res.n=n;
		for(int i=0;i<n;++i) for(int j=0;j<n;++j) (res.a[i+j]+=x.a[i]*a[j])%=mod;
		return res;
	}
}h;

ll qpow(ll x,ll y)
{
	if(y<0)return 0;ll res=1;
	for(;y;y>>=1,x=x*x%mod)if(y&1)res=res*x%mod;
	return res;
}
poly qpow(poly x,ll y)
{
	poly res;res.clear();res.a[0]=1;
	for(;y;y>>=1,x=x*x)if(y&1)res=x*res;
	return res;
}
ll P(int x,int y){if(y<0 || y>x)return 0;return fac[x]*ifac[x-y]%mod;}
int cg(char c){return (c=='Y'?1:0);}

void init()
{
	fac[0]=1;for(int i=1;i<N;++i) fac[i]=fac[i-1]*i%mod;
	for(int i=0;i<N;++i) ifac[i]=qpow(fac[i],mod-2);
}

int main()
{
	freopen("2742.in","r",stdin);
	freopen("2742.out","w",stdout);

	int T=read();init();
	while(T--)
	{
		cnt[1]=cnt[2]=cnt[3]=ans=0;
		n=read();scanf("%s%s",s1,s2);
		for(int i=0;i<n;++i) cnt[cg(s1[i])+cg(s2[i])*2]++;
		//printf("cnt:%d %d %d\n",cnt[1],cnt[2],cnt[3]);
		h.clear();h.n=cnt[3]+1;
		//for(int i=0;i<h.n;++i) printf("%lld ",h.a[i]); puts("");
		for(int i=0;i<=cnt[3];++i)h.a[i]=ifac[i+1];
		//for(int i=0;i<h.n;++i) printf("%lld ",h.a[i]); puts("");
		h=qpow(h,cnt[1]);
		//for(int i=0;i<h.n;++i) printf("%lld ",h.a[i]); puts("");
		ans=h.a[cnt[3]]*fac[cnt[3]]%mod*fac[cnt[1]+cnt[3]]%mod;
		ans=ans*P(cnt[1],cnt[2])%mod*qpow(n-1,cnt[1]-cnt[2])%mod;
		printf("%lld\n",ans);
	}

	return 0;
}

【参考代码2】( O ( n log n ) O(n\log n) )

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=2505,mod=1e9+7;
int n,cnt[4];//1=YN,2=NY,3=YY
char s1[N],s2[N];
ll ans,fac[N],ifac[N];

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

ll qpow(ll x,ll y)
{
	if(y<0)return 0;ll res=1;
	for(;y;y>>=1,x=x*x%mod)if(y&1)res=res*x%mod;
	return res;
}
ll P(int x,int y){if(y<0 || y>x)return 0;return fac[x]*ifac[x-y]%mod;}
ll C(int x,int y){if(y<0 || y>x)return 0;return fac[x]*ifac[y]%mod*ifac[x-y]%mod;}
int cg(char c){return (c=='Y'?1:0);}

void init()
{
	fac[0]=1;for(int i=1;i<N;++i) fac[i]=fac[i-1]*i%mod;
	for(int i=0;i<N;++i) ifac[i]=qpow(fac[i],mod-2);
}

int main()
{
	freopen("2742.in","r",stdin);
	freopen("2742.out","w",stdout);

	int T=read();init();
	while(T--)
	{
		cnt[1]=cnt[2]=cnt[3]=ans=0;
		n=read();scanf("%s%s",s1,s2);
		for(int i=0;i<n;++i) cnt[cg(s1[i])+cg(s2[i])*2]++;
		for(int i=0;i<=cnt[1];++i) 
			(ans+=(((cnt[1]-i)&1)?-1:1)*C(cnt[1],i)%mod*qpow(i,cnt[1]+cnt[3])%mod)%=mod;
		ans=(ans+mod)%mod;
		ans=ans*fac[cnt[3]]%mod*P(cnt[1],cnt[2])%mod*qpow(n-1,cnt[1]-cnt[2])%mod;
		printf("%lld\n",ans);
	}

	return 0;
}

【总结】
妙啊!妙啊!

猜你喜欢

转载自blog.csdn.net/Dream_Lolita/article/details/85301421