处女座和小姐姐(二)

【题目描述】

课上处女座成功将纸条传给了小姐姐,约下午和小姐姐一起逛街。他们坐在公交车上一起欣赏窗外的广告牌,每一个广告牌都有一个编号,而处女座的视野范围是有限的,每次只能看到连续的p个广告牌。由于处女座是数学大师,他用O(1)的时间算出来了他看到的广告牌编号的积mod P的值并记录了下来,直到坐车到了商场。

商场里有定制手环的地方,他可以定制一个长度为k的手环,但是选号是收费的,而处女座把家教挣来的钱都用于外出打比赛了,于是他决定自己挑号。

他把之前路上记下来数字按先从左往右,再从上到下的顺序排成一个n*m的矩阵,他想从中选出一条长度为k的路径,使得路径上的数在mod k的意义上各不相同(每个点只和他的上下左右相邻),从而作为他选的号码。

现在处女座想知道,他有多少条路径可以选择(不忽视顺序,(2,2)->(2,1)和(2,1)->(2,2)会被视为两条路径)。

【输入描述】

输入数据共包括两行,第一行包括四个整数n,m,p,P,k,其中p为处女座一次能看到的连续广告牌的数量,其他字母含义如描述所示。

第二行包括四个整数a0,x,y,z,对于每个广告牌而言,他的编号是ai=x∗ai−12+y∗ai−1+z。广告牌从a1开始到an∗m+p−1为止。

1≤n,m≤20
1≤k≤min(20,n∗m)
1≤p≤106
1≤A0,P,X,Y,Z≤1,000,000,000

【输出描述】

一个整数,表示可以选择的路径数量。

【样例】

示例1

输入
2 2 1 2 2
1 0 1 1
输出
4
说明
a1=2,a2=3,a3=4,a4=5 

所以2*2的矩阵是:

0 1

0 1

因此可行方案有(1,1)->(1,2),(1,2)->(1,1),(2,1)->(2,2),(2,2)->(2,1)共4种

思路:

问题可以分成两个子问题,首先是求一个数组连续 p 个数 mod p 的乘积,其次是找出长度为 k 的符合条件的链的个数

对于第一个问题,可以将序列按照长度 m 进行分段,每段分别计算前缀和后缀乘积,这样任意位置的答案可以看成前段的后缀与后短的乘积拼出

对于第二个问题,可以用 dfs 进行搜索,但由于 (400*3)^20 显然会 TLE,因此可进行双向优化,枚举起点然后双向 dfs,一次向两边进行搜索,故而时间复杂度可降到 400*(2*3)^10,此外,可以使用状压的方式来记录状态

【源代码】

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PI acos(-1.0)
#define E 1e-6
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define N 2000001
#define LL long long
using namespace std;
LL n,m,p,P,k;
LL a[N],b[N],temp[N];
LL X,Y,Z;
LL res;
LL G[101][101];
int dx[]={1,-1,0,0};
int dy[]={0,0,1,-1};
void dfs(int x,int y,int cnt,int ans,int sum,bool flag)
{
    if(cnt==sum){
        if(!flag)
            temp[ans>>1]++;
        if(flag)
            res+=temp[(((1<<(k+1))-1)^ans)>>1];
        return;
    }
    for(int i=0;i<4;i++){
        int nx=x+dx[i];
        int ny=y+dy[i];
        if(ans&(1<pos){
            res=1;
            pos=i;
            b[i]=a[i];
            for(int j=i-1;j>=i-p+1;j--)
                b[j]=b[j+1]*a[j]%P;
        }
        temp[++cnt]=(LL)b[i-p+1]*res%P;
    }

    int cur=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            G[i][j]=temp[++cur]%k+1;


    res=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            memset(temp,0,sizeof(temp));

            //双向优化搜索
            dfs(i,j,1,1|(1<

 

你可能感兴趣的:(#,搜索——深度优先搜索,DFS,#,牛客)