模拟15 题解(waiting)

T1

60%算法

定义f[i][j]表示枚举到i位置,已经使用过了j个队,

$f[i][j]+=f[i-1][t] ( t \in [max(0,j-k),j])$滚动一下

这是个O(n^3)的,考虑如何优化,发现可以使用前缀和,避免枚举t,$O(n^2)$

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<algorithm>
 6 #define R register
 7 #define ll long long
 8 using namespace std;
 9 inline int read()
10 {
11     int f=1,x=0;char ch=getchar();
12     while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
13     while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
14     return f*x;
15 }
16 const int mod=998244353;
17 const int maxn=10000005;
18 int n,m,k;
19 ll f[2][maxn],g[2][maxn];
20 int main()
21 {
22     //freopen("data1","r",stdin);
23     //freopen("1.out","w",stdout);
24     n=read(),m=read(),k=read();
25     m-=n;k--;
26     f[1][0]=g[1][0]=1;
27     for(R int j=1;j<=k;++j)
28     {
29         f[1][j]=1;
30         g[1][j]=(g[1][j-1]+f[1][j])%mod;
31     }
32     for(int j=k+1;j<=m;j++)
33         g[1][j]=g[1][j-1]%mod;
34     R int cnt=1;
35     for(R int i=2;i<=n;++i)
36     {
37         cnt^=1;
38         for(R int j=0;j<=m;++j)
39         {
40             R int l=max(0,j-k),r=j;
41             R ll tot=0;
42             if(l==0)tot=g[cnt^1][r];
43             else tot=(g[cnt^1][r]-g[cnt^1][l-1]+mod)%mod;
44             f[cnt][j]=tot%mod;
45             g[cnt][j]=f[cnt][j]%mod;
46             if(j)  (g[cnt][j]+=g[cnt][j-1])%=mod;
47         }
48     }
49     printf("%lld\n",f[cnt][m]%mod);
50 }
View Code

 

100%算法

问题转化成:m个物品,放到n个抽屉里,每个至少放一个,最多放k个

若任何的限制: C(m+n-1,n-1)表示一共有m个物品,分成n组就要用n-1个挡板,把挡板也看成空位,总共m+n-1个空位,选出来n-1个

若考虑至少放一个:C(m-n+n-1,n-1)先用n个物品给每个抽屉放一个,剩了m-n个物品,再加上n-1个空,剩下的同上

再考虑k的限制:C(n,i)*C(m-n-i*k+n-1,n-1)表示至少有i个的数量已经超过k(>=k+1)所以先给n个抽屉放一个之后,再给n个放上k个,使之成为k+1个

就是m-n-i*k,再加上n-1个空

数组开2e7就行,显然n>m直接return0

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstring>
 5 #include<algorithm>
 6 #define R register
 7 #define ll long long
 8 using namespace std;
 9 inline ll read()
10 {
11     ll f=1,x=0;char ch=getchar();
12     while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
13     while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
14     return f*x;
15 }
16 const ll mod=998244353;
17 const ll maxn=20000005;
18 ll fac[maxn],inv[maxn],facinv[maxn];
19 ll n,m,k;
20 void init()
21 {
22     fac[1]=1;
23     inv[0]=inv[1]=facinv[0]=facinv[1]=1;
24     for(ll i=2;i<=m+n;i++)
25     {
26         fac[i]=fac[i-1]*i*1ll%mod;
27         inv[i]=(mod-mod/i)*inv[mod%i]%mod;
28         facinv[i]=facinv[i-1]*inv[i]%mod;
29     }
30 }
31 ll C(ll n,ll m)
32 {
33     if(n<m)    return 0;
34     return 1ll*fac[n]*facinv[n-m]%mod*facinv[m]%mod;
35 }
36 int main()
37 {
38     n=read(),m=read(),k=read();
39     if(n>m||n*k<m){puts("0");return 0;}
40     init();
41     ll ans=C(m-1,n-1)%mod;
42     for(ll i=1;i<=n;i++)
43     {
44         if(m-n<i*k)break;
45         ll f=(i&1)?-1:1;
46         ans=(ans+1ll*f*C(m-i*k-1,n-1)%mod*C(n,i)%mod+mod)%mod;
47     }
48     printf("%lld\n",ans%mod);
49 }
View Code

T2

对于无环的情况,最优解就是,图中的最长链的长度,,,为什么?

注意审题:只是炸城市,道路不炸,故某一城市毁了,其他城市的联通性不变,所以最长链上最少要炸的次数就是链长,而其他的路径,当然可以在炸最长链上的每个节点的同时也一起炸,有环的话,tarjan所点成scc,然后拓扑排序,把入度为0的节点放入队列中,枚举其子节点,子节点的答案为,父节点答案加上子节点的scc大小,并且这个点可能使用多次,所以要每次取最大值

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cmath>
  4 #include<cstring>
  5 #include<algorithm>
  6 #include<vector>
  7 #include<queue>
  8 using namespace std;
  9 inline int read()
 10 {
 11     int f=1,x=0;char ch=getchar();
 12     while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
 13     while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
 14     return f*x;
 15 }
 16 const int maxn=1000005;
 17 int n,m;
 18 struct node{
 19     int v,nxt;
 20 }e[2*maxn];int h[maxn],nu;
 21 void add(int x,int y)
 22 {
 23     e[++nu].v=y;
 24     e[nu].nxt=h[x];
 25     h[x]=nu;
 26 }
 27 struct nodc{
 28     int v,nxt;
 29 }ec[maxn*2];int hc[maxn],nuc;
 30 void add_c(int x,int y)
 31 {
 32     ec[++nuc].v=y;
 33     ec[nuc].nxt=hc[x];
 34     hc[x]=nuc;
 35 }
 36 int dfn[maxn],low[maxn],num,top,cnt;
 37 int sta[maxn],ins[maxn],bl[maxn];
 38 vector<int>scc[maxn];
 39 void tarjan(int x)
 40 {
 41     dfn[x]=low[x]=++num;
 42     sta[++top]=x,ins[x]=1;
 43     for(int i=h[x];i;i=e[i].nxt)
 44     {
 45         int y=e[i].v;
 46         if(!dfn[y])
 47         {
 48             tarjan(y);
 49             low[x]=min(low[x],low[y]);
 50             
 51         }
 52         else if(ins[y])
 53             low[x]=min(low[x],dfn[y]);
 54     }
 55     if(dfn[x]==low[x]){
 56         int y;cnt++;
 57         do{
 58             y=sta[top--],ins[y]=0;
 59             scc[cnt].push_back(y);bl[y]=cnt;
 60         }while(y!=x);
 61     }
 62 }
 63 int ind[maxn],ans[maxn];
 64 void topo()
 65 {
 66     queue<int>q;
 67     for(int i=1;i<=cnt;i++)
 68         if(!ind[i])
 69             q.push(i),ans[i]=scc[i].size();
 70     while(q.size())
 71     {//cout<<" ^^^";
 72         int x=q.front();q.pop();
 73         for(int i=hc[x];i;i=ec[i].nxt)
 74         {
 75             int y=ec[i].v;ind[y]--;
 76             if(!ind[y])q.push(y);
 77             int w=scc[y].size();
 78             if(ans[y]<ans[x]+w)ans[y]=ans[x]+w;
 79         }
 80     }
 81 }
 82 int main()
 83 {
 84     //freopen("data","r",stdin);
 85     n=read(),m=read();
 86     for(int i=1;i<=m;i++)
 87     {
 88         int x=read(),y=read();
 89         add(x,y);
 90     }
 91 //    cout<<"^^^";
 92     for(int i=1;i<=n;i++)
 93         if(!dfn[i])
 94             tarjan(i);
 95     for(int x=1;x<=n;x++)
 96     {
 97         for(int i=h[x];i;i=e[i].nxt)
 98         {
 99             int y=e[i].v;
100             if(bl[x]!=bl[y])
101                 add_c(bl[x],bl[y]),ind[bl[y]]++;
102         }
103     }
104     topo();
105     int an=0;
106     for(int i=1;i<=n;i++)
107         an=max(ans[i],an);
108     printf("%d\n",an);    
109 }
View Code

 

转载于:https://www.cnblogs.com/casun547/p/11329764.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值