【2018.07.04】集训模拟赛第二场

对应计蒜客模拟赛Day2:T1  T2  T3

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;
}

猜你喜欢

转载自blog.csdn.net/qq_39791208/article/details/80910792