ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Atcoder 236

2022-03-05 17:34:38  阅读:153  来源: 互联网

标签:Atcoder le ... int void 236 dp define


AtCoder Beginner Contest 236

\(Ex.Distinct Multiples\)

题意

给定\(N\)个数\(D=(D_1,D_2,...,D_N)\)和一个正整数\(M\),问有多少种方法构造一个长度为\(N\)的序列\(A=(A_1,A_2,...,A_N)\),使得\(A\)满足以下条件:

  • \(1\le A_i\le M\)
  • \(A_i\neq A_j\) \((i\neq j)\)
  • \(D_i|A_i\)

\(2\le N\le 16\) \(1\le M\le 10^{18}\) \(1\le D_i\le M\)

Sol

组合数学

由于\(N\)很小,所以可以考虑用状态压缩。

以这\(N\)个数有几个数相同划分集合,用二进制来表示,那么根据容斥原理,假设当前的集合是\(S\),假设\(S\)在二进制下第\(i_1,i_2,...,i_m\)位是\(1\),那么容斥系数就是\((-1)^{m-1}\times(m-1)!\times \lfloor \frac{M}{LCM(D_{i_1},D_{i_2},...,D_{i_m})}\rfloor\)

#include <bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define mod 998244353
#define pb push_back
using namespace std;

template <typename T> void rd (T &x)
{
    x=0;int f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-') f=-1;s=getchar();}
    while(s>='0'&&s<='9') x=x*10+(s^48),s=getchar();
    x*=f;
}
template <typename T> void chkMax(T &x, T y) { if (y > x) x = y; }
template <typename T> void chkMin(T &x, T y) { if (y < x) x = y; }
const int N=1<<17;
ll fac[N];
int cal(int s)
{
  int cnt=0;
  for(int i=0;i<=17;i++)
    if((s>>i)&1) cnt++;
  return cnt; 
}
int lowbit(int s){return s&-s;}
int main()
{
    fac[0]=1;
    int n;
    ll M;
    rd(n),rd(M);
    for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
    vector<ll>D(n),xs(n+1);
    for(int i=0;i<n;i++) rd(D[i]);
    for(int i=1;i<=n;i++) //xs[i]=(i%2?1:-1)*fac[i-1]%mod;
    if(i%2) xs[i]=fac[i-1];
    else xs[i]=(mod-fac[i-1])%mod;
    vector<ll>g(1<<n);
    for(int s=0;s<(1<<n);s++)
    {
          ll v=1;
          for(int i=0;i<n;i++)
            if((s>>i)&1)
            {
               ll o=__gcd(v,D[i]);
               if(v*1.0*D[i]/o>2E18)
               {
                  v=M+1;
                  break;
               }
               v=v*D[i]/o;//求lcm
            }
        g[s]=(M/v%mod*xs[cal(s)])%mod;
        if(g[s]<0) g[s]=(g[s]+mod)%mod;
        
    } 
    vector<ll>dp(1<<n);
    dp[0]=1;
    for(int s=1;s<(1<<n);s++)
    {   
        int ns=s&~lowbit(s); //去掉末尾1
        for(int t=ns;;t=(t-1)&ns) //每次去掉1个末尾的1
        {
          dp[s]=(dp[s]+g[t|lowbit(s)]*dp[ns^t])%mod;   //ns^t是只有去掉的1的保留的值
          if(!t) break;
        }
    }
    cout<<dp.back()<<'\n';
    return 0;
}

\(D.Good Vertices\)

题意

有一个有\(N\)个点的有向图,现在总共有\(T\)秒,每\(1\)秒会加入一条有向边。定义一个点\(v\)是漂亮的:从点\(1\)可以到点\(v\)并且经过且只经过\(L\)条边。

现在每一个点\(v\),如果它不是漂亮的点,输出\(-1\),否则输出这个点\(v\)最早被认为是漂亮的点的时间。

\(2\le N\le 100\) \(1\le T\le N^2\) \(1\le N\le 10^9\) 不会出现重边

Sol

把加入的时间当做边权建立一个完全有向图,对于没有加入的边,边权定义为\(inf\)

定义\(dp[i][v]\)表示从点\(1\)到点\(v\)经过且只经过\(i\)条边的最小边权

初始状态:对于点\(2,3,...,N\),\(dp[0][i]=inf\) \(dp[0][1]=0\)

转移就是:\(dp[i][v]=min_{1\le u\le N}max\{dp[i-1][u],w(u,v)\}\)

为什么这里去\(max\)是因为满足条件的边如果后加那么这个时间就应该是后加的时间,比如从点\(1\)到\(u\)走\(i-1\)步,第\(i\)步走\(w(u,v)\),如果前\(i-1\)步中存在大于第\(i\)步的时间,那么这条路径的最早时间应该是这条路径上的最大权值,但我们要求的是最早时间,所以对所有满足条件的路径取\(min\)

继续优化:

将取\(min\)看做加法,将取\(max\)看做乘法

那么\(dp[i][v]=\sum_{u=1}^N(dp[i-1][u]\times w(u,v))\)

写成矩阵的形式

#include <bits/stdc++.h>
#define ll long long
#define inf 0x7f7f7f7fll
#define fi first
#define se second
#define mod 998244353
#define pb push_back
using namespace std;

template <typename T> void rd (T &x)
{
    x=0;int f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-') f=-1;s=getchar();}
    while(s>='0'&&s<='9') x=x*10+(s^48),s=getchar();
    x*=f;
}
template <typename T> void chkMax(T &x, T y) { if (y > x) x = y; }
template <typename T> void chkMin(T &x, T y) { if (y < x) x = y; }
const int N=105;
int n,T,L;
struct matrix
{
    int a[N][N];
    matrix(){memset(a,inf,sizeof a);}
    matrix operator * (const matrix &t)
    {
        matrix C;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                for(int k=1;k<=n;k++)
                    C.a[i][j]=min(C.a[i][j],max(a[i][k],t.a[k][j]));
        return C;
    }
};
int main()
{
    rd(n),rd(T),rd(L);
    matrix A,B;
    for(int i=1;i<=T;i++){
        int u,v;
        rd(u),rd(v);
        B.a[u][v]=i;
    }  
    A.a[1][1]=0;
    while(L)
    {
        if(L&1) A=A*B;
        B=B*B;
        L>>=1;
    }
    for(int i=1;i<=n;i++)
        if(A.a[1][i]==inf) cout<<-1<<' ';
        else cout<<A.a[1][i]<<' ';
    return 0;

       
}

标签:Atcoder,le,...,int,void,236,dp,define
来源: https://www.cnblogs.com/Arashimu0x7f/p/15968697.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有