T1:“看到兔子,立刻想到数列,立刻想到斐波那契,立刻想到递推,立刻想到矩阵快速幂,OIer的思想唯有在这一层能够如此跃进。”——鲁迅《我没说过》
本题中因为兔子会在10岁时产完崽死掉,所以我们可以用一个1*11的矩阵来记录0~10岁的兔子数量。
转移也很好想,首先1~10岁的兔子数量都等同于上一岁的兔子数量,即a[1]=a[0],a[2]=a[1],......,a[10]=a[9]。
而0岁的兔子就是1~10岁的兔子之和,即a[0]=a[1]+a[2]+a[3]+...+a[10]。
这样转移的矩阵就出来了。
初始矩阵:
转移矩阵:
快速幂转移即可。
代码:
#include<bits/stdc++.h>
#define mod 1000000007
using namespace std;
typedef long long LL;
int read()
{
char c=getchar();int f=1,sum=0;
while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
int n;
LL ans;
struct matrix{
int a,b;
LL s[15][15];
matrix(){
memset(s,0,sizeof(s));
a=b=0;
}
void setone()
{
for(int i=0;i<11;i++)
s[i][i]=1;
}
};
matrix operator *(matrix x,matrix y)
{
matrix ans;
ans.a=x.a;ans.b=y.b;
for(int i=0;i<x.a;i++)
for(int j=0;j<y.b;j++)
for(int k=0;k<x.b;k++)
ans.s[i][j]=(ans.s[i][j]+x.s[i][k]*y.s[k][j]%mod)%mod;
return ans;
}
matrix ksm(int p,matrix x)
{
matrix ret;
ret.setone();
ret.a=11;ret.b=11;
while(p)
{
if(p&1) ret=ret*x;
x=x*x;
p>>=1;
}
return ret;
}
int main()
{
n=read();
matrix start,p;
start.a=1,start.b=11;
start.s[0][1]=1;
p.a=11,p.b=11;
for(int i=1;i<=9;i++) p.s[i][0]=p.s[i-1][i]=1;
p.s[10][0]=1;
start=start*ksm(n-1,p);
for(int i=0;i<11;i++)
ans=(ans+start.s[0][i])%mod;
printf("%lld\n",ans);
return 0;
}
T2:区间逆序对问题。
快速求区间逆序对可以用树状数组和线段树来求,显然树状数组更简单一些。
对于一个区间[l,r],如果我们已知该区间的答案,那么我们能够很容易的求出[l-1,r],[l+1,r],[l,r-1],[l,r+1]的答案。
以求[l-1,r]为例,因为我们新加进来了a[l-1],所以答案要加上[l,r]里面比a[l-1]小的个数,即树状数组的sum(a[l-1]-1),
剩下的自己推推就好。
数据范围就是让你不用把询问排序的......因此本题不需要用到莫队的曼哈顿最小生成树思想。(但用了更快)
代码:
#include<bits/stdc++.h>
#define maxn 30005
using namespace std;
typedef long long LL;
int read()
{
char c=getchar();int f=1,sum=0;
while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
int n,m,a[maxn],ans[maxn];
int c[maxn];
void add(int x,int v){for(x;x<=n;x+=x&-x) c[x]+=v;}
int sum(int x){int ret=0;for(x;x;x-=x&-x) ret+=c[x];return ret;}
struct node{
int l,r,id;
}q[maxn];
int block,bel[maxn];
bool cmp(node x,node y)
{
return bel[x.l]==bel[y.l]?x.r<y.r:bel[x.l]<bel[y.l];
}
int main()
{
n=read();
block=(int)(sqrt(n));
for(int i=1;i<=n;i++)
{
a[i]=read();
bel[i]=(i-1)/block+1;
}
m=read();
for(int i=1;i<=m;i++)
q[i].l=read(),q[i].r=read(),q[i].id=i;
sort(q+1,q+1+m,cmp);
int nl=1,nr=1;add(a[1],1);
int ret=0;
for(int i=1;i<=m;i++)
{
while(nl<q[i].l){add(a[nl],-1);ret-=sum(a[nl]-1);nl++;}
while(nr<q[i].r){nr++;ret+=(sum(n)-sum(a[nr]));add(a[nr],1);}
while(nr>q[i].r){add(a[nr],-1);ret-=(sum(n)-sum(a[nr]));nr--;}
while(nl>q[i].l){nl--;ret+=sum(a[nl]-1);add(a[nl],1);}
ans[q[i].id]=ret;
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}
T3:数据范围很小,一看就是状压。
对于每个村民,都有三种状态:0代表在原地,1代表在背上,2代表在重点。
因此用dp[state][x][y]表示当前村民状态是state,人在(x,y)所需的最小时间,其中state是三进制数。
有一个细节:到了终点时放人有两种决策:一种是全放下,另一种是只放下会让自己速度减慢的村民。因此在到达重点时,两种决策都要考虑。
用普通的不能再普通的bfs转移即可,dp数组其实只起到一个记录的作用。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int read()
{
char c=getchar();int f=1,sum=0;
while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
int n,m,k,num;
char s[15][15];
char p[15];
int speed[15];
int sx,sy,ex,ey;
int fx[4]={-1,1,0,0},fy[4]={0,0,-1,1};
int bit[15];
int dp[60005][15][15];
struct node{
int x,y,state;
};
int cal(int sta)
{
int wei=0,sped=k;
memset(bit,0,sizeof(bit));
while(sta)
{
wei++;
bit[wei]=sta%3;
sta/=3;
}
for(int i=1;i<=wei;i++) if(bit[i]==1) sped+=speed[i];
return max(1,sped);
}
bool judge(int x,int y)
{
return (x>=1 && x<=n && y>=1 && y<=m);
}
int bei(int sta,int x,int y)//背人
{
int wei=0,pos;
for(int i=1;i<=num;i++)
if(p[i]==s[x][y])
{
pos=i;
break;
}
memset(bit,0,sizeof(bit));
while(sta)
{
wei++;
bit[wei]=sta%3;
sta/=3;
}
if(bit[pos]) return 0;
bit[pos]=1;
for(int i=num;i>=1;i--)
sta=sta*3+bit[i];
return sta;
}
int fang1(int sta)//全放
{
int wei=0;
memset(bit,0,sizeof(bit));
while(sta)
{
wei++;
bit[wei]=sta%3;
sta/=3;
}
for(int i=1;i<=wei;i++)
if(bit[i]==1) bit[i]=2;
for(int i=wei;i>=1;i--)
sta=sta*3+bit[i];
return sta;
}
int fang2(int sta)//只放速度减慢的
{
int wei=0;
memset(bit,0,sizeof(bit));
while(sta)
{
wei++;
bit[wei]=sta%3;
sta/=3;
}
for(int i=1; i<=wei;i++)
if(bit[i]==1 && speed[i]>0) bit[i]=2;
for(int i=wei;i>=1;i--)
sta=sta*3+bit[i];
return sta;
}
void bfs()
{
queue<node> q;
node st;
st.x=sx;st.y=sy;st.state=0;
q.push(st);
while(!q.empty())
{
node now=q.front();
q.pop();
int nowspeed=cal(now.state);
int dis=dp[now.state][now.x][now.y]+nowspeed;
for(int i=0;i<4;i++)
{
int nex=now.x+fx[i];
int ney=now.y+fy[i];
if(judge(nex,ney))
{
if(s[nex][ney]!='#' && dp[now.state][nex][ney]>dis)
{
dp[now.state][nex][ney]=dis;
q.push((node){nex,ney,now.state});
}
if(s[nex][ney]>='A' && s[nex][ney]<='Z')
{
int nexstate=bei(now.state,nex,ney);
if(dp[nexstate][nex][ney]>dis)
{
dp[nexstate][nex][ney]=dis;
q.push((node){nex,ney,nexstate});
}
}
if(s[nex][ney]=='t')
{
int sta1=fang1(now.state);
int sta2=fang2(now.state);
if(dp[sta1][nex][ney]>dis)
{
dp[sta1][nex][ney]=dis;
q.push((node){nex,ney,sta1});
}
if(dp[sta2][nex][ney]>dis)
{
dp[sta2][nex][ney]=dis;
q.push((node){nex,ney,sta2});
}
}
}
}
}
}
int main()
{
n=read();m=read();k=read();
for(int i=1;i<=n;i++)
scanf("%s",s[i]+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(s[i][j]>='A' && s[i][j]<='Z') num++;
if(s[i][j]=='s') sx=i,sy=j;
if(s[i][j]=='t') ex=i,ey=j;
}
for(int i=1;i<=num;i++)
{
char c=getchar();
while(c<'A' || c>'Z') c=getchar();
speed[i]=read();
p[i]=c;
}
memset(dp,0x3f,sizeof(dp));
dp[0][sx][sy]=0;
bfs();
int sta=1;
for(int i=1;i<=num;i++)
sta*=3;
printf("%d\n",dp[sta-1][ex][ey]);
return 0;
}