zoukankan      html  css  js  c++  java
  • noip模拟测试6


    T1:那一天我们许下约定

    n块饼干,d天内分完,每天分的块数小于m,求方案数。

    ( n,m<=2000 , d<=1012 )

     1:dp ,设计状态发 f[i][j] 表示前i天分j块的方案数,f[i][j]= ∑ f[i-1][k] ( j-m < k < j )

      发现复杂度为 O ( nd ) ,由于d过大,复杂度爆炸,30分再见;

      发现实际中状态大多数是冗余的(因为好多天都不会分饼干)

      所以更改状态设计,f[i][j] 表示前 i 个分了饼干的天,总共分出 j 块的方案数。

      答案为 ∑ f[i][n]*C(d,i)

      复杂度 O ( n2 )

       

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 #include<vector>
     7 #define ll long long
     8 using namespace std;
     9 const int MAXN=2005,D=998244353;
    10 ll n,m,d,ans;
    11 
    12 ll f[MAXN][MAXN],g[MAXN][MAXN];
    13 
    14 ll fac[MAXN],inv[MAXN];
    15 
    16 ll qpow(ll x,ll k)
    17 {
    18     ll ret=1;
    19     while(k)
    20     {
    21         if(k&1) ret=(ret*x)%D;
    22         x=(x*x)%D;
    23         k>>=1;
    24     }
    25     return ret%D;
    26 }
    27 
    28 ll C(ll x,ll y)
    29 {
    30     ll tmp=1;
    31     for(int i=0;i<y;i++)
    32         tmp=(tmp*((d-i)%D))%D;
    33     return (tmp*inv[y])%D;
    34 }
    35 
    36 int main()
    37 {
    38     while(1)
    39     {
    40         scanf("%lld%lld%lld",&n,&d,&m);
    41         if(!n&&!m&&!d) break;
    42         
    43         for(int i=0;i<=n;i++) g[0][i]=1;
    44         for(int i=1;i<=min(n,d);i++)
    45         {
    46             for(int j=1;j<=n;j++)
    47                 if(j-m>=0) f[i][j]=((g[i-1][j-1]-g[i-1][j-m])%D+D)%D;
    48                 else f[i][j]=g[i-1][j-1];
    49             for(int j=1;j<=n;j++)
    50                 g[i][j]=(g[i][j-1]+f[i][j])%D;
    51         }
    52         
    53         fac[0]=inv[0]=fac[1]=1;
    54         for(int i=2;i<=n;i++)
    55             fac[i]=(fac[i-1]*i)%D;
    56         inv[n]=qpow(fac[n],D-2);
    57         for(int i=n-1;i>=1;i--)
    58             inv[i]=(inv[i+1]*(i+1))%D;
    59         
    60         ans=0;
    61         for(int i=1;i<=min(n,d);i++)
    62             ans=(ans+f[i][n]*C(d,i))%D;
    63         printf("%lld
    ",ans);
    64         memset(f,0,sizeof(f));
    65         memset(g,0,sizeof(g));
    66     }
    67     return 0;
    68 }
    t1 Code

     2:机房某大佬 TKJ 指出,这不就是组合加容斥吗?

      简单说一下

      枚举分饼干的天数,利用1中的优化可将复杂度优化到 O ( n ) 

      先插板法计算如果没有每天小于m的限制的答案。

      再容斥计算出有m限制的答案。

      具体不细说了

      详情见大佬TKJ的博客:zto TKJ orz



    T2:那一天她离我而去

      n个点m条边的无向图,求经过点1的最小环,保证没有重边、自环

      ( n,m 104 )

      一脸懵比?

      floyd最小环??

      n3 过 一万???

      于是爆0,gg

     1:先想暴力怎么做 ( floyd

      因为必须经过1节点,所以这个环的路径应该是1->x---最短路--->y->1(x与y均与1相连)

      哦,那吼了,暴力枚举和1相连的点,全跑一遍不经过1的最短路

      复杂度O ( n2logm),继续爆炸

      

      怎么优化?

      仔细一想会发现刚刚暴力枚举点会有很多冗余

      所以从枚举这里优化

      首先想如果把所有与1相连的点分成两组,其中一组为起点,另一组为终点

        (在跑最短路时,将所有起点的距离初值设为它到1的距离,最后用所有终点的 [最短路值 + 到1的距离] 来更新答案)

      

      这样我们就有了一种对于两组点之间的计算方法

      这时我们只要考虑如何将这些与1相连的点分成两组,且对于任意两点,一定会被至少一次分在两个不同的组中

      哦,然后就是一种船新的思路——二进制拆分

      

      我们把所有与1相连的点标号

      然后枚举标号二进制下的每一位,将该位为0的分为一组,1的分为另一组

      因为对于任意两个点,它们的标号一定不同,所以一定至少一次被分到两个组中

      所以正确,那就没了。

      复杂度为 O ( n logm logn )

      

      1 #include<cstdio>
      2 #include<iostream>
      3 #include<cmath>
      4 #include<cstring>
      5 #include<algorithm>
      6 #include<queue>
      7 #include<vector>
      8 using namespace std;
      9 const int MAXN=10005,MAXM=40005;
     10 int T;
     11 int n,m,ans;
     12 
     13 struct node
     14 {
     15     int to,nxt,dis;
     16 }mp[MAXM*2];
     17 int h[MAXN],tot;
     18 void add(int x,int y,int z)
     19 {
     20     mp[++tot].nxt=h[x];
     21     mp[tot].to=y;
     22     mp[tot].dis=z;
     23     h[x]=tot;
     24 }
     25 
     26 inline int R()
     27 {
     28     int a=0;char c=getchar();
     29     while(c>'9'||c<'0') c=getchar();
     30     while(c>='0'&&c<='9') a=a*10+c-'0',c=getchar();
     31     return a;
     32 }
     33 
     34 int vec[MAXN],vd[MAXN],cnt,s[MAXN],sd[MAXN],ps,t[MAXN],td[MAXN],pt;
     35 
     36 int dis[MAXN];
     37 bool vis[MAXN];
     38 
     39 void dijikstra()
     40 {
     41     memset(vis,0,sizeof(vis));
     42     memset(dis,0x3f,sizeof(dis));
     43     priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
     44     for(int i=1;i<=ps;i++)
     45         q.push(make_pair(sd[i],s[i])),dis[s[i]]=sd[i];
     46     vis[1]=1;
     47     while(!q.empty())
     48     {
     49         int u=q.top().second; q.pop();
     50         if(vis[u]) continue;
     51         vis[u]=1;
     52         for(int i=h[u];i;i=mp[i].nxt)
     53         {
     54             int v=mp[i].to,ds=mp[i].dis;
     55             if(vis[v]) continue;
     56             if(dis[v]>dis[u]+ds)
     57             {
     58                 dis[v]=dis[u]+ds;
     59                 q.push(make_pair(dis[v],v));
     60             }
     61         }
     62     }
     63     for(int i=1;i<=pt;i++)
     64         ans=min(ans,dis[t[i]]+td[i]);
     65 }
     66 
     67 void first()
     68 {
     69     ans=0x3f3f3f3f;
     70     cnt=tot=0;
     71     memset(h,0,sizeof(h));
     72 }
     73 
     74 int main()
     75 {
     76     T=R();
     77     while(T--)
     78     {
     79         first();
     80         n=R();m=R();
     81         for(int i=1,aa,bb,cc;i<=m;i++)
     82         {
     83             aa=R();bb=R();cc=R();
     84             add(aa,bb,cc);
     85             add(bb,aa,cc);
     86         }
     87         for(int i=h[1];i;i=mp[i].nxt)
     88             vec[++cnt]=mp[i].to,vd[cnt]=mp[i].dis;
     89         
     90         if(!cnt)
     91         {
     92             printf("-1
    ");
     93             continue;
     94         }
     95         
     96         for(int i=1,j=1;j<=15;i<<=1,j++)
     97         {
     98             ps=pt=0;
     99             for(int j=1;j<=cnt;j++)
    100                 if(j&i) s[++ps]=vec[j],sd[ps]=vd[j];
    101                 else t[++pt]=vec[j],td[pt]=vd[j];
    102             if(!ps||!pt) continue;
    103             dijikstra();
    104         }
    105         if(ans!=0x3f3f3f3f) printf("%d
    ",ans);
    106         else printf("-1
    ");
    107     }
    108     return 0;
    109 }
    t2 Code


      T3:哪一天她能重回我身边

      给定n张牌,每张牌正反面分别有一个数字,通过反转使所有正面的数字各不相同,求最小反转次数和方案数

      ( n <= 105 数字<=2*n )

      

     1:不知为什么想到建图

      然后在牌正反面的数字间连边,会惊奇的发现反转卡牌等价与反转边的方向,而结果是要使所有点的出度都小于等于1

      原图应为多个联通块

      考虑一个联通块,会发现只有边数小于等于点数才会有解

      所以只可能是树或基环树

      发现一种合法的反转应该是使所有的边都指向父亲  (基环树是所有的边都指向环,环上的边同为顺时针或逆时针)

      

      于是想到换根dp

      第一次dp出以任意点u的答案  (只需要计算让所有边都指向父亲需要反转多少次)

      第二次dp出以每个点为根的答案

      取最小值,统计最小值的方案次数

      特别的: 基环树的方案只可能有1或两种,只需要判断顺逆时针的反转次数是否相同即可

          (具体来说只需要判断以环上相邻两点分别为根的答案是否相等即可)

      

      最后将每个联通块的最小值相加,方案数相乘

      复杂度为 O ( n )

      

      1 #include<cstdio>
      2 #include<iostream>
      3 #include<cstring>
      4 #include<algorithm>
      5 #include<cmath>
      6 #include<vector>
      7 #define ll long long
      8 using namespace std;
      9 const int MAXN=100100,D=998244353;
     10 int T;
     11 int n;
     12 
     13 struct node
     14 {
     15     int to,nxt;
     16 }mp[MAXN*2];
     17 int h[MAXN*2],tot;
     18 void add(int x,int y)
     19 {
     20     mp[++tot].nxt=h[x];
     21     mp[tot].to=y;
     22     h[x]=tot;
     23 }
     24 
     25 bool flag;
     26 int vcnt,ecnt;
     27 
     28 bool vis[MAXN*2];
     29 
     30 void dfs(int u)
     31 {
     32     vcnt++;vis[u]=1;
     33     for(int i=h[u];i;i=mp[i].nxt)
     34     {
     35         int v=mp[i].to;
     36         ++ecnt;
     37         if(vis[v]) continue;
     38         dfs(v);
     39     }
     40 }
     41 
     42 int s,nxt,id;
     43 ll num,f[MAXN*2],g[MAXN*2],ans,anscnt;
     44 
     45 void dfs1(int u,int fa)
     46 {
     47     vis[u]=1;
     48     for(int i=h[u];i;i=mp[i].nxt)
     49     {
     50         int v=mp[i].to;
     51         if(v==fa) continue;
     52         if(vis[v])
     53             s=u,nxt=v,id=i;
     54         else
     55         {
     56             dfs1(v,u);
     57             f[u]+=f[v]+(i&1);
     58         }
     59     }
     60 }
     61 
     62 vector<int> vv;
     63 
     64 void dfs2(int u,int fa)
     65 {
     66     vv.push_back(g[u]);
     67     for(int i=h[u];i;i=mp[i].nxt)
     68     {
     69         int v=mp[i].to;
     70         if(v==fa) continue;
     71         if(i==id||i==(id^1)) continue;
     72         g[v]=g[u]+((i&1)?-1:1);
     73         dfs2(v,u);
     74     }
     75 }
     76 
     77 int main()
     78 {
     79     scanf("%d",&T);
     80     while(T--)
     81     {
     82         tot=1;flag=0;
     83         memset(h,0,sizeof(h));
     84         memset(vis,0,sizeof(vis));
     85         scanf("%d",&n);
     86         for(int i=1,aa,bb;i<=n;i++)
     87         {
     88             scanf("%d%d",&aa,&bb);
     89             add(bb,aa);add(aa,bb);
     90         }
     91         for(int i=1;i<=2*n;i++)
     92             if(!vis[i])
     93             {
     94                 vcnt=ecnt=0;
     95                 dfs(i);
     96                 if(ecnt/2>vcnt)
     97                 {
     98                     printf("-1 -1
    ");
     99                     flag=1;
    100                     break;
    101                 }
    102             }
    103         if(flag) continue;
    104         memset(vis,0,sizeof(vis));
    105         memset(f,0,sizeof(f));
    106         memset(g,0,sizeof(g));
    107         ans=0;anscnt=1;
    108         for(int i=1;i<=2*n;i++)
    109             if(!vis[i])
    110             {
    111                 vv.clear();
    112                 s=nxt=id=0;
    113                 num=0;
    114                 
    115                 dfs1(i,0);
    116                 g[i]=f[i];
    117                 dfs2(i,0);
    118                 
    119                 if(!id)
    120                 {
    121                     sort(vv.begin(),vv.end());
    122                     for(int j=0;j<vv.size();j++)
    123                         if(vv[j]!=vv[0]) break;
    124                         else ++num;
    125                     ans+=vv[0];
    126                 }
    127                 else
    128                 {
    129                     id&=1;
    130                     if(g[s]+(id^1)==g[nxt]+id) num=2;
    131                     else num=1;
    132                     ans+=min(g[s]+(id^1),g[nxt]+id); 
    133                 }
    134                 anscnt=anscnt*num%D;
    135             }
    136         printf("%lld %lld
    ",ans,anscnt);
    137     }
    138     return 0;
    139 }
    t3 View

  • 相关阅读:
    贪婪算法、递归计算、动态规划背包问题
    递归、动态规划计算斐波那契数列
    用于确定两个字符串最长公共子串的函数
    快速排序算法
    顺序、二分查找文本数据
    MyBatis面试题
    Spring面试题
    SpringMvc面试题
    jsp和servlet面试
    EJB的理解
  • 原文地址:https://www.cnblogs.com/Gkeng/p/11220788.html
Copyright © 2011-2022 走看看