【2016 Multi-University Training Contest 6】【1002】【A Simple Chess】

Problem Description

There is a n×m board, a chess want to go to the position
(n,m) from the position (1,1) .
The chess is able to go to position (x2,y2) from the position (x1,y1) , only and if only x1,y1,x2,y2 is satisfied that (x2x1)2+(y2y1)2=5, x2>x1, y2>y1 .
Unfortunately, there are some obstacles on the board. And the chess never can stay on the grid where has a obstacle.
I want you to tell me, There are how may ways the chess can achieve its goal.

Input

The input consists of multiple test cases.
For each test case:
The first line is three integers, n,m,r,(1n,m1018,0r100) , denoting the height of the board, the weight of the board, and the number of the obstacles on the board.
Then follow r lines, each lines have two integers, x,y(1xn,1ym) , denoting the position of the obstacles. please note there aren't never a obstacles at position (1,1) .

Output

For each test case,output a single line "Case #x: y", where x is the case number, starting from 1. And y is the answer after module 110119 .
 

Sample Input

  
  
1 1 0 3 3 0 4 4 1 2 1 4 4 1 3 2 7 10 2 1 2 7 1
 

Sample Output
  
  
Case #1: 1 Case #2: 0 Case #3: 2 Case #4: 1 Case #5: 5

题意:走日子格的棋子(类似马)要从(1,1)格跳到(N,M)格,(N,M<=10^18),棋盘上有至多100个路障。起点不会是路障。


思路:多校赛上次有个算棋子的题四种情况其中就有这个马的情况,不过是算先手后手必胜策略。这次思路类似。首先棋盘太大肯定是不能从下标下手的,但是路障只有100个,我们就挨个考虑路障。那么在无路障的情况下要怎么从一点到另一点呢?日子格满足横纵坐标分别加2加1或者加1加2,也就是说ΔX=x2-x1和ΔY=y2-y1应该满足:

1.ΔX=2a+b ΔY=2b+a 其中a是走横2格纵1格,b是纵2格横1格。

2.共方案数应该为C(a+b)(a),只是每次走时横纵抉择上的不同。

那么我们需要一个计算大组合数的方法:Lucas定理

那么解决了白棋盘的情况——任意两点没有障碍的方案数都可以很快得出了——我们怎么处理100个障碍呢?

100个?xjb搞哇!……我一开始就想到了是容斥,计算第i个障碍处之前经过了j个障碍的方案数,j为偶数就减去,为奇数就加上,维护最后的总方案数。

但是GG了……幸亏WZJRJ28很清醒,想到了另一种类似容斥的方法:计算起点到障碍点的方案数,并计算减去该点对之后点的影响。——当然这样需要对所有障碍建图,每个障碍对其所有的右下的障碍连边。——这大概也是我前一个方法orz的原因吧,由于懒没敲建图直接xjbDP,O(N^3)不说还逻辑上出了毛病……坑了队友一波……还好成老师回老家结婚也不忘记帮忙,帮忙切了两个打表题,让我们队进了前200……万恶的罚时TAT


代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<deque>
using namespace std;
typedef long long ll;
int p=110119;
ll N,M,Ans;
int tot,R;

int effect[110];
vector <int> G[110];
deque <int> Q;

ll obx[110],oby[110];
ll st[110],en[110];
ll dis[110][110];
ll f[112000];
void init(int p)
{                 //f[n] = n!
    f[0] = 1;
    for (int i=1; i<=p; ++i) f[i] = f[i-1] * i % p;
}
ll pow_mod(ll a, ll x, int p)
{
    ll ret = 1;
    while (x)   {
        if (x & 1)  ret = ret * a % p;
        a = a * a % p;
        x >>= 1;
    }
    return ret;
}
ll Lucas(ll n, ll k, int p)
{       //C (n, k) % p
     ll ret = 1;
     while (n && k) {
        ll nn = n % p, kk = k % p;
        if (nn < kk) return 0;                   //inv (f[kk]) = f[kk] ^ (p - 2) % p
        ret = ret * f[nn] * pow_mod (f[kk] * f[nn-kk] % p, p - 2, p) % p;
        n /= p, k /= p;
     }
     return ret;
}

ll dp[101];
ll WALK(ll n,ll m)
{
    if(n<=0 || m<=0)
        return 0;
    if((n+m)%3!=0)
        return 0;
    ll X=n-(n+m)/3;
    ll Y=m-(n+m)/3;
    if(X<0 || Y<0)
        return 0;
    return Lucas(X+Y,X,p);
}
int main()
{
    init (p);
    int t=0;
    ll tempx,tempy;
    bool flag,WTF;
    while(scanf("%I64d%I64d%d",&N,&M,&tot)!=EOF)
    {
        WTF=false;
        memset(dp,0,sizeof(dp));
        memset(st,0,sizeof(st));
        memset(en,0,sizeof(en));
        memset(dis,0,sizeof(dis));
        for(int i=1;i<=100;i++)
            G[i].clear();
        R=0;
        for(int i=1;i<=tot;i++)
        {
            scanf("%I64d%I64d",&tempx,&tempy);
            if(tempx==N&&tempy==M)WTF=true;
            flag=false;
            for(int j=1;j<=R;j++)
            {
                if(tempx==obx[j]&&tempy==oby[j])
                    flag=true;
                if(tempx>N || tempy>M)
                    flag=true;
            }
            if(!flag)
            {
                R++;
                obx[R]=tempx;
                oby[R]=tempy;
            }
        }
        R++;
        obx[R]=N;
        oby[R]=M;
        
        memset(effect,0,sizeof(effect));
        for(int i=1;i<=R;i++)
        {
            for(int j=1;j<=R;j++)
            {
                if(obx[i]<obx[j]&&oby[i]<oby[j])
                {
                    G[i].push_back(j);
                    effect[j]++;
                }
            }
        }
        
        for(int i=1;i<=R;i++)
        {
            st[i]=WALK(obx[i]-1,oby[i]-1);
            en[i]=WALK(N-obx[i],M-oby[i]);
        }
        for(int i=1;i<=R;i++)
            for(int j=1;j<=R;j++)
                dis[i][j]=WALK(obx[j]-obx[i],oby[j]-oby[i]);
        
        for(int i=1;i<=R;i++)
            if(effect[i]==0)
                Q.push_back(i);
        
        for(int i=1;i<=R;i++)
            dp[i]=st[i];
        
        int len,temp;
        while(!Q.empty())
        {
            len=Q.size();
            
            for(int k=1;k<=len;k++)
            {
                temp=Q.front();
                Q.pop_front();
                
                for(unsigned int i=0;i<G[temp].size();i++)
                {
                    dp[G[temp][i]]-=(dp[temp]*dis[temp][G[temp][i]])%p;
                    dp[G[temp][i]]+=p;
                    dp[G[temp][i]]%=p;
                    effect[G[temp][i]]--;
                    if(effect[G[temp][i]]==0)
                        Q.push_back(G[temp][i]);
                }
            }
            
        }
        
        Ans=dp[R];
        
        if(N==1&&M==1)
            Ans=1;
        if(WTF)
            Ans=0;
        printf("Case #%d: %I64d\n",++t,Ans);
    }
    return 0;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值