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 }
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 }
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 }