Wannafly挑战赛17

Wannafly挑战赛17

A 走格子

题意:稍微有点歧义,碰到边界或或者走过的点的意思是如果下一个点是边界(x==0||y==0||x==n+1||y==n+1)或者走过的点,先逆时针转90度再走。

思路:模拟  当前方向和坐标。

#include <bits/stdc++.h>
using namespace std;
const long long INF = 0x3f3f3f3f;
int x,y,n,m;
bool vis[1100][1100];
int dir=1;
int main()
{
        while(cin>>n>>m)
        {
            memset(vis,0,sizeof(vis));
            int x=1,y=1;
            vis[x][y]=1;
            dir=1;
            while(m--)
            {
                if(dir==1)
                {
                    x++;
                    if(vis[x][y]==1||x==n+1)
                    {
                        x--;
                        y++;
                        vis[x][y]=1;
                        dir=2;
                    }
                }
                else if(dir==2)
                {
                    y++;
                    if(vis[x][y]==1||y==n+1)
                    {
                        y--;
                        x--;
                        vis[x][y]=1;
                        dir=3;
                    }
                }
                else if(dir==3)
                {
                    x--;
                   if(vis[x][y]==1||x==0)
                    {
                        x++;
                        y--;
                        vis[x][y]=1;
                        dir=4;
                    }
                }
                else if(dir==4)
                {
                    y--;
                    if(vis[x][y]==1||y==0)
                    {
                        y++;
                        x++;
                        vis[x][y]=1;
                        dir=1;
                    }
                }
                
 
            }
            cout<<x<<" "<<y<<endl;
        }

}

B 求值2

题意:从二重循环可以看出求是二项式系数平方的和

思路:存在公式  C(2n, n)=ΣC(n,i)*C(n,i)  (0<=i<=n)

#include <bits/stdc++.h>
using namespace std;
const long long INF = 0x3f3f3f3f;
typedef long long ll;
ll a,b,n;
ll ans;
const ll mod=998244353;
ll fast(ll a,ll b)
{
    ll ans=1;
    while(b>0)
    {
        if(b%2==1) ans=ans*a%mod;
        a=a*a%mod;
        b=b/2;
    }
    return ans;
}
int main()
{
        while(cin>>n)
        {
            a=1,b=1;
            ans=0;
            for(ll i=1;i<=n;i++)
            {
                a=a*i%mod;
                b=b*(2*i-1)%mod*(2*i)%mod;
                ll ls=b*fast(a,mod-2)%mod*fast(a,mod-2)%mod;
                ans=(ans+ls)%mod;
            }
            cout<<ans%mod<<endl;
        }
}

C 简单环

题意:找简单环 类TSP问题

思路:N只有20 考虑状压DP   dp[i][j] 表示以i为结尾 当前状态为j的路径数 枚举下一个经过的点进行状态转移。

   在转移中,为了防止重复,下一个经过的点要大于起点  。

     注意到这样的做法 同一个简单环会计算两次(顺逆时针) 最后答案要除以2(逆元处理) 

    时间复杂度O(n^2*2^n)

#include <bits/stdc++.h>
using namespace std;
const long long INF = 0x3f3f3f3f;
typedef long long ll;
const ll MOD=998244353,inv=(MOD+1)/2;
ll dp[21][1<<20],ans[25];
bool e[25][25];
vector<int >G[25];
int n,m,k;
int main()
{
        cin>>n>>m>>k;
        while(m--)
        {
            int u,v;
            cin>>u>>v;
            u--,v--;
            if(u==v) continue;
            G[u].push_back(v);
            G[v].push_back(u);
            e[u][v]=e[v][u]=1;
        }
        for(int i=0;i<n;i++)
            dp[i][1<<i]=1;
        for(int i=1;i<(1<<n);i++)//枚举当前状态
        {
            int t=__builtin_ctz(i);//末尾0的个数=标号最小的点=起点
            for(int j=t;j<=n;j++)//枚举当前路径结尾
            {
                 if ((i>>j&1)&&dp[j][i])//结尾到达过且路径数不为0
                {
                    for (int k:G[j])//枚举下一个结尾
                        if (k>t&&(i>>k&1)==0)//大于起点(防止重复)且未经过 更新dp
                            (dp[k][i|(1<<k)]+=dp[j][i])%=MOD;
                    if (e[t][j])//起点和终点相连 构成简单环
                        (ans[__builtin_popcount(i)]+=dp[j][i])%=MOD;
                }
            }
        }
        ans[1]=ans[2]=0;
        for (int i=0;i<k;i++)
        {
            ll sum=0;
            for (int j=1;j<=n;j++)
                if (j%k==i)
                    (sum+=ans[j])%=MOD;
           cout<<sum*inv%MOD<<endl;
        }
    return 0;
}

  

E 跳格子

题意:题目讲的很清楚了

思路:首先,从后往前跳等价于从前往后跳。要求第二次跳的点必须在第一次中跳到过。

   那么我们可以先处理出从前往后跳i步的方案数a[i];

   然后考虑第二次跳,跳到i点的方案数b[i]等于b[i-j]*a[j](1<=j<=m) 转移一下即可。

  

#include<bits/stdc++.h>
#define MAXN 524288
#define P 233333333
#define ll long long
using namespace std;
int t,n,m,N,i,j,a[20],b[400005];
//b[j] 跳到j的方案数
//a[i] 跳i步的方案数 递推
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&N,&n,&m);
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        for(a[0]=i=1;i<=m;i++)
            for(j=1;j<=i&&j<=n;j++)
                a[i]=(a[i]+a[i-j])%P;
        for(b[0]=i=1;i<=N;i++)
            for(j=1;j<=i&&j<=m;j++)
                b[i]=(b[i]+(ll)b[i-j]*a[j])%P;
        cout<<b[N]<<endl;
    }
    return 0;
}

  

 

posted @ 2018-06-09 12:05  zerocentury  阅读(169)  评论(0编辑  收藏  举报