【前言】
原题是
的,然而懒得找。
T1
【题目】
一个
的二维矩阵,第
行第
列的价值是
,初始起点在第
行第
列的格子,要走
步,最终回到起点,每一步可以走四相邻的格子。每进入一个格子可以得到该格子的价值,可以重复进入相同格子,而且重复进入可以得到当前格子。求能获得最大价值。
,且
是偶数
【解题思路】
我们可以
一下答案的组成,那么一定是沿着一条路径走,然后反复横跳,再沿着路径走回去。
因为所有路径最长只有
,于是我们可以暴力迭代前
步,剩下的步数直接计算即可。
【参考代码】
#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
【题目】
有一个
的网格,在
秒内每秒会有一场灾难,用
来表示,意为
,将
上的数置零,否则加一。初始每个格子上的数字为
,问
秒后所有数之和。
【解题思路】
我们可以对每一条横线进行考虑,那么我们要考虑的就是一个线段与圆交的问题,这个可以通过简单计算完成。我们现在得到每条斜线上的若干个交点,用一个
来维护计算这条斜线上最终所有数字的和即可。
【参考代码】
#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
【题目】
有
个人,每个人可以挑战人也可以被挑战,现在已知每个人是否挑战人和是否被挑战成功。挑战是有时间顺序的。当一个人被挑战成功后,接下来所有对他的挑战都会失败,同时他将不再会挑战别人。一个人最多挑战一次人,求满足要求的方案数。
【解题思路】
有
个人,每个人可以挑战人也可以被挑战,现在已知每个人是否挑战人和是否被挑战成功。挑战是有时间顺序的。当一个人被挑战成功后,接下来所有对他的挑战都会失败,同时他将不再会挑战别人。一个人最多挑战一次人,求满足要求的方案数。
这道题初看十分神奇,因为需要满足的顺序限制很多,而且“挑战成功了还可以被继续挑战”这种限制更是难办,然后好像就做不了了。
但细细分析一下,我们可能可以得到一些可行的转化。
我们可以将这些挑战看作一个个序列,每一个序列均由
开头,后面跟着若干个
,最后由
或
或
结尾。
为什么可以这样考虑?其实需要抓住一个关键,结尾的人若是
,显然是可以的;若是
或
,那它不论挑战什么都是一样的,因为都失败了。
这里似乎没有考虑
,然而确实不用考虑。
所以我们的思路大概就出来了:给每个 序列分配若干个 ,然后补上 ,最后将多出来的 任意分配。
以下我们设 表示 的个数, 表示 的个数。(为什么没有 ?因为它是 的个数)
我们首先考虑知道了每个 序列分配了多少个 序列时我们怎么统计贡献?那么实际上就是一个经典的排列问题,设个数分别为 个,答案就是
我们设
表示前
个
,一共分配了
个
的方案数。则:
其中
,具体意义是我们分配了
个
,然后还要除以一个顺序,
是因为还有开头的
。
那么观察到阶乘项与
的第二维长得十分像,我们进行移项,可以得到:
这样令
,
,我们可以得到
这显然是个卷积的形式,于是:
这里
开始为
,那么我们快速幂暴力卷积就可以做到
了,当然如果你用
优化一下就是
了。
不过我们还能继续优化,而且写起来更简单。
观察到我们要求的实际上是
,而这个形式十分优美:
约定
根据泰勒展开
,那么我们实际上就是要将
去掉常数项再左移一项,于是:
将它展开:
由于右边有一个
,实际上每有一个相当于要求的幂次
于是我们将
去掉后,我们现在要求的是这个东西的
次方的系数
于是我们再展开
就可以得到答案了。
这样复杂度就是
的了(快速幂的
)
【参考代码1】( )
#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】( )
#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;
}
【总结】
妙啊!妙啊!