[考试反思]0308省选模拟40:观察
今天考试的时候教练在10:30之后把语言从$C++11$改成了$C++$
并且在$OJ$的比赛界面上写了编译参数。
然而在$pdf$里浪的我并没有察觉到什么不对。
嗯。。。你知道我想表达什么。。
又是蓝色的$0$啊啊啊。。。我估计我是联赛后CE次数最多的了。。
如果不CE的话会多$22pts$。是$rk3$。
细节写挂了,特判是树的时候我写的是点数等于边数$-1$。不然是$50pts$,那就是$rk2$了
然而没有“如果”。缺心眼就是缺心眼,这次长个心眼就是了。
部分分很足的一次考试。提示意义也挺强。很不错的一套题。
这次$T1$的思路能想到一部分,但是“从本质上思考”还是差不少的。
然后$T2$的话猜到可以是生成函数奈何不熟练,而另一个思路完全不可想,拿暴力跑路。
$T3$是个部分分给的挺明确的网络流,但是连想带写花的时间还是有点多了。
T1:染色问题
大意:联通无向图,有$k$种颜色,节点染色,每条边两端颜色不同。求方案数。$n \le 10^5,m \le n+5,k \le 10^5$
发现$m$比$n$大的不多,而又是联通的,所以离树结构也差不多。
首先对于小的点,我们可以搜索,当颜色很大时,采用最小表示法就可以了。
对于一棵树,答案就是$k(k-1)^{n-1}$
对于一个基环树,如果我们钦定环上某一个点的颜色之后,发现环之外的部分每个点的贡献就是答案乘$k-1$。
也就是说我们可以不断去掉度数为$1$的点。
这样只剩下一个环。发现环不好做的原因就是如果你当成序列来做,首尾可能会相同。
于是我们做$dp$记目前为止链两端颜色是否相同时的方案数。就可以做了。
然后发现,这样的话你的dp值就已经转移到链上了,只与链的两个端点有关。
那么一条链就是除了两端以外,度数均为$2$。而我们也可以发现对于一个度数为$2$的点,它两侧点的$dp$值可以合并。
设$f$为两端相同,$g$为不同,那么有$f=f_0f_1+(k-1)g_0g_1,g=f_0g_1+f_1g_0+(k-2)g_0g_1$
所以这个点消失,两条边也合并成一条新的边。
这样,图里也不存在度数为$2$的点了。
因为$m-n \le 5$所以图中度数$\geq 3$的点很少,就可以进行搜索了。
对于三元环,我们最后把它缩成$2$个点会比较好处理。
支持加边与删边的话,直接用数组复杂度不对,$map/vector$貌似可以用,但是用$set$代码会更好写。
(不要再想$unordered$啦,关于$C++11$,它死了)
听$LNC$说可以用$mutable$,好巨啊,代码好写且常数小。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mod 1000000007 4 #define S 100005 5 struct P{ 6 int to,f,g; 7 P(){} P(int a,int b,int c): to(a),f(b),g(c){} 8 friend bool operator<(P x,P y){return x.to<y.to||(x.to==y.to&&x.g+x.f<y.g+y.f);} 9 };multiset<P>s[S]; 10 int n,m,k,rt=1,ans,co[S],A[S],q[S],ald[S],t; 11 void sch(int al,int alc){ 12 if(al==t+1){ 13 int a=1; 14 for(int i=1;i<=t;++i)for(set<P>::iterator it=s[q[i]].begin();it!=s[q[i]].end();++it) 15 if((*it).to>=q[i])a=1ll*a*(co[q[i]]==co[(*it).to]?(*it).f:(*it).g)%mod; 16 ans=(ans+1ll*a*rt%mod*A[alc])%mod; 17 return; 18 }for(int i=1;i<=k&&i<=alc+1;++i)co[q[al]]=i,sch(al+1,max(alc,i)); 19 } 20 int main(){//freopen("0.in","r",stdin); 21 cin>>n>>m>>k; 22 for(int i=A[0]=1;i<=n||i<=k;++i)A[i]=A[i-1]*(k-i+1ll)%mod; 23 for(int i=1,a,b;i<=m;++i){ 24 scanf("%d%d",&a,&b); 25 s[a].insert(P(b,0,1));s[b].insert(P(a,0,1)); 26 } 27 for(int i=1;i<=n;++i)if(s[i].size()==1)q[++t]=i; 28 for(int h=1,p;p=q[h],h<=t;++h)if(s[p].size()==1){ 29 int u=(*s[p].begin()).to; s[u].erase(s[u].lower_bound(P(p,0,0))); 30 if(s[u].size()==1)q[++t]=u; ald[p]=1; rt=rt*(k-1ll)%mod; 31 } 32 for(int i=1;i<=n;++i)if(s[i].size()==2){ 33 P x=*s[i].begin(),y=*(--s[i].end()); 34 if(x.to==y.to)continue; ald[i]=1; 35 s[x.to].erase(s[x.to].lower_bound(P(i,0,0))); 36 s[y.to].erase(s[y.to].lower_bound(P(i,0,0))); 37 s[x.to].insert(P(y.to,(1ll*x.f*y.f+(k-1ll)*x.g%mod*y.g)%mod,((k-2ll)*x.g%mod*y.g+1ll*x.f*y.g+1ll*x.g*y.f)%mod)); 38 s[y.to].insert(P(x.to,(1ll*x.f*y.f+(k-1ll)*x.g%mod*y.g)%mod,((k-2ll)*x.g%mod*y.g+1ll*x.f*y.g+1ll*x.g*y.f)%mod)); 39 }t=0; 40 for(int i=1;i<=n;++i)if(!ald[i])q[++t]=i; 41 sch(1,0); 42 cout<<ans<<endl; 43 }
T2:IOer
大意:给定$n,m,u,v$求$\sum\limits_{A}[\sum\limits_{x=1}^{m} A_x = n] \prod\limits_{x=1}^{m} (v_ui)^{A_i}$。$n \le 10^{18},m\le 2 \times 10^5,u,v \le 10^9$
好像没啥可说的,题解思路太清奇了。难点就在于这个题意转化。
好懂不好想。
很对就是了。粘过来了。
也可以用除了$LNC$以外谁都没听明白的生成函数来搞,是大量的推式子。
几个思路的重点是:将完全相同的元素忽略它们的内部顺序要求,最后除阶乘。
以及将“分界点”具体化也当成球,类似于插板的思想,这样就能强制它出现。
其实没什么新东西,但是就是难想到。抖机灵了
然而代码很好写倒是真
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mod 998244353 4 #define S 200005 5 int qp(int b,long long t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;} 6 long long n;int t,m,u,v,fac[S],inv[S],ans; 7 int main(){ 8 for(int i=fac[0]=1;i<S;++i)fac[i]=1ll*fac[i-1]*i%mod; 9 inv[S-1]=qp(fac[S-1],mod-2); 10 for(int i=S-2;~i;--i)inv[i]=inv[i+1]*(i+1ll)%mod; 11 cin>>t;while(t--){ 12 cin>>n>>m>>u>>v;ans=0; 13 for(int i=0;i<m;++i)ans=(ans+(i&1?mod-1ll:1ll)*inv[m-1-i]%mod*inv[i]%mod*qp((1ll*(m-i)*u+v)%mod,n+m-1))%mod; 14 cout<<1ll*ans*qp(u,(m-1ll)*(mod-2))%mod<<endl; 15 } 16 }
T3:deadline
大意:$n$天$m$任务。每个任务有种类$0/1$,有$k$对关系形如:第$u$天可以做第$v$个任务。
要求你指定一个计划表约定每天要做$0/1$类任务中的哪一种,然后对方会在满足计划表前提下尽可能多做任务。最小化最终完成任务的数量。
$n,m \le 2000,k\le 5000$
一个挺直觉的网络流。最小割。
所有$0$类任务连源,$1$类任务连汇,然后对于每一天建两个点连$1$边。然后任务向对应的天连边。
如果一天的边不想被断开,也就是如果这一天不想做任务,那么就要求当天的$0/1$类任务中至少一种被都做完了,也就是割掉了。
所以是对的。网络流不说复杂度。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 66666 4 int n,m,k,t[S],o[S][2],pc,q[S],d[S],v[S],fir[S],l[S],to[S],ec=1,ans; 5 void link(int a,int b,int w){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=w;} 6 void con(int a,int b){link(a,b,1);link(b,a,0);} 7 bool bfs(){ 8 for(int i=1;i<=pc;++i)d[i]=pc+1; 9 for(int h=1,t=1;h<=t;++h)for(int i=fir[q[h]];i;i=l[i])if(v[i]&&d[to[i]]>d[q[h]]+1) 10 d[q[++t]=to[i]]=d[q[h]]+1; 11 return d[pc]<=pc; 12 } 13 int dfs(int p,int f){ 14 if(p==pc)return f; int r=f; 15 for(int i=fir[p];i&&r;i=l[i])if(v[i]&&d[to[i]]==d[p]+1){ 16 int x=dfs(to[i],1); 17 if(!x)d[to[i]]=-1; 18 else v[i]--,v[i^1]++,r--; 19 }return f-r; 20 } 21 int main(){ 22 scanf("%d%d%d",&n,&m,&k); 23 pc=n; 24 for(int i=1;i<=m;++i)o[i][0]=++pc,o[i][1]=++pc,con(pc-1,pc); 25 ++pc; 26 for(int i=1;i<=n;++i)scanf("%d",&t[i]); 27 for(int i=1;i<=n;++i)if(t[i])con(i,pc);else con(0,i); 28 for(int i=1,x,y;i<=k;++i){ 29 scanf("%d%d",&y,&x); 30 if(t[y])con(o[x][1],y); 31 else con(y,o[x][0]); 32 } 33 while(bfs())ans+=dfs(0,n); 34 cout<<ans<<endl; 35 }