[考试反思]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 }
View Code

 

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

 

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

 

posted @ 2020-03-08 21:47  DeepinC  阅读(206)  评论(0编辑  收藏  举报