2019-8-9 考试总结
A. 建设城市
很熟悉的题面,和"那一天我们许下约定"差不多。
$n^2$暴力很好想,$70$分也很好拿。
但是没拿到。。
正解是个容斥。
用总的方案数减去不合法的方案数。
不合法的也就是建设队大于等于$k+1$的。
然后就容斥。
$ans=C_{m-1}^{n-1}-\sum \limits_{i=1}^{m-ik-1>=0} (-1)^iC_n^i\times C_{m-ik-1}^{n-1}$。
左边那个式子是个挡板,不考虑$k$的限制,每个城市至少有$1$的方案数。
右边是个容斥,表示至少$i$个城市不合法的方案数。
从$m$个建设队中选出$ik$个给$i$个城市,然后每个城市再分$1$,最后的那些随便分。
在用一个挡板法。
所以最后$C$的底数是$m-ik-n+n-1$,也就是$m-ik-1$。
然后再乘一个$C_n^i$。
就是这个式子。
丑陋的代码:
#include<iostream> #include<cstring> #include<cstdio> #define Maxn 10000050 #define Reg register #define mod 998244353 #define int long long #define _max(x,y) ((x)>(y)?(x):(y)) #define C(x,y) (((x)<(y))?0:(fac[x]*inv[y]%mod*inv[(x)-(y)]%mod)) using namespace std; int n,m,k,fac[Maxn],inv[Maxn],lss[Maxn]; signed main() { scanf("%lld%lld%lld",&n,&m,&k); fac[0]=inv[0]=fac[1]=inv[1]=lss[1]=1; if(n>m) printf("0"); else { for(Reg int i=2;i<=m;++i) { fac[i]=fac[i-1]*i%mod; lss[i]=(mod-mod/i)*lss[mod%i]%mod; inv[i]=inv[i-1]*lss[i]%mod; } int ans=C(m-1,n-1); for(Reg int i=1,cur;i<=n;++i) { if(m-i*k-1<0) break; cur=(i&1)?-1:1; ans=(ans+cur*C(n,i)%mod*C(m-i*k-1,n-1)%mod+mod)%mod; } printf("%lld",(ans+mod)%mod); } return 0; }
B. 轰炸行动
正解:
$Tarjan$缩$scc$,然后跑拓扑找最长链。
好吧,当时想到了$Tarjan$和拓扑,
没想到要找最长链。。
这个题只要找最长链的节点个数就可以了。
因为每一条链上的点都要一个一个炸。
丑陋的代码:
#include<iostream> #include<cstring> #include<cstdio> #include<vector> #include<queue> #define Maxn 10000050 #define Reg register #define int long long #define _min(x,y) ((x)<(y)?(x):(y)) #define _max(x,y) ((x)>(y)?(x):(y)) using namespace std; int n,m,tot=0,top=0,fir[Maxn],fic[Maxn]; int cnt,indx,low[Maxn],dfn[Maxn],stack[Maxn],ins[Maxn],bel[Maxn],sum[Maxn]; int du[Maxn],dep[Maxn]; struct Tu {int st,ed,next;} lian[Maxn],liap[Maxn]; void add(int x,int y) { lian[++tot].st=x; lian[tot].ed=y; lian[tot].next=fir[x]; fir[x]=tot; return; } void adp(int x,int y) { liap[++top].st=x; liap[top].ed=y; liap[top].next=fic[x]; fic[x]=top; ++du[y]; return; } void tarjan(int x) { low[x]=dfn[x]=++indx; stack[++stack[0]]=x; ins[x]=1; for(Reg int i=fir[x];i;i=lian[i].next) { if(!dfn[lian[i].ed]) { tarjan(lian[i].ed); low[x]=_min(low[x],low[lian[i].ed]); } else if(ins[lian[i].ed]) low[x]=_min(low[x],dfn[lian[i].ed]); } if(low[x]==dfn[x]) { ++cnt; while(stack[stack[0]]!=x) { bel[stack[stack[0]]]=cnt; ins[stack[stack[0]--]]=0; } bel[stack[stack[0]]]=cnt; ins[stack[stack[0]--]]=0; } return; } void topsort() { queue<int> q; int p=1; for(Reg int i=1;i<=cnt;++i) { if(!du[i]) { q.push(i); dep[i]=sum[i]; p=_max(p,dep[i]); } } while(!q.empty()) { int x=q.front(); q.pop(); // cout<<x<<' '<<sum[x]<<' '<<dep[x]<<endl; p=_max(p,dep[x]); for(Reg int i=fic[x];i;i=liap[i].next) { --du[liap[i].ed]; dep[liap[i].ed]=_max(dep[liap[i].ed],dep[x]+sum[liap[i].ed]); if(du[liap[i].ed]==0) { q.push(liap[i].ed); } } } printf("%lld",p); return; } signed main() { // freopen("text.in","r",stdin); indx=cnt=tot=top=0; scanf("%lld%lld",&n,&m); for(Reg int i=1,x,y;i<=m;++i) { scanf("%lld%lld",&x,&y); add(x,y); } for(Reg int i=1;i<=n;++i) if(!dfn[i]) tarjan(i); for(Reg int i=1;i<=n;++i) { ++sum[bel[i]]; // cout<<bel[i]<<' '; } // cout<<endl; for(Reg int i=1;i<=tot;++i) if(bel[lian[i].st]!=bel[lian[i].ed]) adp(bel[lian[i].st],bel[lian[i].ed]); topsort(); return 0; }
C. 石头剪刀布
挺难的,我不会。
总结:
看到$T1$基本上是原题,而且我又什么都想不起来,心态近乎爆炸。
最后连$n^2$暴力的分都没拿到。
说是低错,就是没水平。。。
$T2$用$Tarjan$缩$scc$很好想。
拓扑排序找最长链这比较难想。
考试一碰见图论和方案计数$dp$和概率期望就死了。
$T3$用玄学$dfs$和特判水到$20$分。
$T1$和$T2$就惨了。
最后$20+0+20=40$。
没什么水平。。。