[考试反思]1218省选模拟2: 告诫

CCF NOI 2018温馨提示:

做题千万条,读题第一条。

多测不清空,爆零两行泪。

将CCF的告诫敬给愚蠢的自己。T1多测清空没清干净AC代码爆零了。

两个打IOI赛制的得分是170/135。如果我清空了勉强可以和skyh持平。

但是没有如果。结果就是「菜」

千万长记性。

还是习惯性的利用了「没有赋值的数组初值为0」的特性,但是清空不彻底就导致崩盘了。

上来三道题,T1是noip模拟测试3的原题,没看出来,也不会做。但是换题了。

然后换上了一个弱化改编版的《无限之环》,想都没想就开始写(我颓题解颓的那么认真),过样例就交,然后我就以为我AC了。

手模了好几个一组测试数据的样例,全过了,很kx。

因为文件的问题重交了一遍,其实是40分钟就写完了。

然后看当时跳过了《无限之环》的miku在我旁边苦写插头dp,感觉他好可怜。

然而其实可怜的是我好歹人家还有40分。。。

然后就结束了。T2一点想法没有,T3写了一个全排列。

然后既然T1已经觉得自己AC了,T2啥也不会,那么也只能想T3的部分分了啊。

看到$a_i$只有2种的那个点,于是就发现状态有很多冗余,有些长得完全一样的全排列就不需要枚举多次了啊。

然后继续优化这个想法,反正剩下的时间也没事干,博一博信仰写个记忆化搜索搜出多少是多少啊

期望得分10分的我没什么想法,看着应该AC了的T1和等同于空着的T2,不知道何去何从。

于是我回到LCT2专题把LCA那道题AC了(差分维护前缀和的区间操作区间查询树状数组真是帅啊!)

调了那么就原来我的树状数组没有锅,题目点编号从0开始,代码里有一处忘+1了无良出题人

然后考试就结束了,惊奇的发现自己T1TLE0了。

我是傻子2333

T1:铁轨建设

题目大意:网格图。用若干回路完全覆盖所有非障碍格,有些关键点,在关键点上路径为直线时产生1代价。问最小代价。多组测试数据。n,m<=25

简化改编的《无限之环》。只有L型与直线型。

我们先把所有点拆成4个表示上下左右,然后把图黑白染色,黑连源白连汇。

每个连源的格点有2流量,连汇的也是。如果图满流了那么就是形成了回路。

考虑怎么翻转/掰直铁轨。我们先假定所有铁轨都是向右与向上的,现在夹角是90度。

夹角是90度时是弯铁轨,代价为0。夹角为180度时是直铁轨,代价为0。

我们发现,如果把铁轨的一支翻转180度,翻转后夹角依旧是90度,是弯铁轨,不贡献代价。所以右向左,上向下连边,流量1费用0。

如果翻转铁轨的一支90度,那么翻转之后夹角是180度,就是直铁轨了,有1代价。所以右向下连边,上向左连边,流量1费用1。

然后就是一个最小费用最大流了。

 1 #include<cstdio>
 2 int min(int a,int b){return a<b?a:b;}
 3 int A[28][28],n,m,t,o[28][28][4],pc,ec=1,S,E,cost,maxflow,out,in,al[2555];
 4 int fir[2555],l[666666],to[666666],w[666666],c[666666],q[6666666],iq[2555],dt[2555];
 5 void link(int a,int b,int W,int C){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;w[ec]=W;c[ec]=C;}
 6 void con(int a,int b,int W,int C){if(a==0||b==0)return;link(a,b,W,C);link(b,a,0,-C);}
 7 bool bfs(){
 8     for(int i=1;i<=pc;++i)dt[i]=1234567890,al[i]=0;q[1]=S;dt[S]=0;
 9     for(int h=1,t=1;h<=t;iq[q[h]]=0,++h)for(int i=fir[q[h]];i;i=l[i])if(w[i]&&dt[to[i]]>dt[q[h]]+c[i]){
10         dt[to[i]]=dt[q[h]]+c[i];
11         if(!iq[to[i]])iq[q[++t]=to[i]]=1;
12     }return dt[E]!=1234567890;
13 }
14 int dfs(int p,int flow){int r=flow;
15     if(p==E)return r;al[p]=1;
16     for(int i=fir[p];i&&r;i=l[i])if(dt[to[i]]==dt[p]+c[i]&&!al[to[i]]&&w[i]){
17         int x=dfs(to[i],min(r,w[i]));
18         if(!x)dt[to[i]]=1234567890;
19         w[i]-=x;w[i^1]+=x;r-=x;cost+=x*c[i];
20     }al[p]=0;return flow-r;
21 }
22 main(){freopen("railway.in","r",stdin);freopen("railway.out","w",stdout);
23     scanf("%d",&t);
24     while(t--){
25         scanf("%d%d",&n,&m);
26         #define O(x) o[i][j][x]
27         for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",&A[i][j]);
28         for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(A[i][j])for(int k=0;k<4;++k)O(k)=++pc;
29         S=++pc;E=++pc;
30         for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(A[i][j])
31             if(i+j&1)con(S,O(0),1,0),con(S,O(1),1,0),con(O(0),O(2),1,0),con(O(1),O(3),1,0),con(O(0),O(3),1,A[i][j]-1),con(O(1),O(2),1,A[i][j]-1);
32             else con(O(0),E,1,0),con(O(1),E,1,0),con(O(2),O(0),1,0),con(O(3),O(1),1,0),con(O(3),O(0),1,A[i][j]-1),con(O(2),O(1),1,A[i][j]-1);
33         for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(i+j&1)if(A[i][j])
34             con(O(0),o[i-1][j][2],1,0),con(O(1),o[i][j+1][3],1,0),con(O(2),o[i+1][j][0],1,0),con(O(3),o[i][j-1][1],1,0);
35         while(bfs())maxflow+=dfs(S,66666);
36         for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(A[i][j])if(i+j&1)out+=2;else in+=2;
37         if(in!=out||in!=maxflow)puts("-1");else printf("%d\n",cost);
38         for(int i=1;i<=pc;++i)fir[i]=0;maxflow=out=in=cost=pc=0;ec=1;
39         for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)for(int k=0;k<4;++k)o[i][j][k]=0;
40     }
41 }
View Code

T2:圈地游戏

咕?部分分都不会写写什么题解?

T3:组合数学

题目大意:给定数列a,对于其所有排列b求前缀和得到s,求$\sum\limits_{b=permutation(a)} \frac{m!}{\prod\limits_{i=2}^{n} s_i}$。m为a之和。n<=100,m<=1000

这种题的式子不少时候还是有含义的,用含义猜测式子有时会比较方便。

可以看到,这个式子的形式比较像可重排列数(感谢%%%LNC纠正定义)。所以从排列的角度出发来思考。

这里分母上是前缀和,而且还缺了一项$s_1$让人挺烦的,先把它补回来。

考虑外层求和记号下$permutation(a)$的含义,就是从那么多个玩意里依次选出来一种。

把m个球进行排列,每种$b_i$个,我们已知每种球的第一个出现的位置,它们的先后顺序就是排列p。

还是需要粘题解。它实在太正确了无法解释。(其实就是懒得手抄一遍)

上面的概率是针对于m个球的任意排列。所以是用$m!$乘上每一个概率,就是合法的方案数了。

特别的,因为题目的式子里并没有$s_1$,所以我们并不限制最后一种球的位置最靠前的那一个,它的编号恰好是1。

这样含义总算是出来了,现在我们抛掉原来含枚举排列的难以优化的式子,只从含义出发,去发现新的复杂度正确的式子。

首先,优先考虑特殊元素——那个位置最靠前的不一定是编号为1的那种球。

我们枚举它到底是那一种,暂时把它的种类编号成为last,这种球里出现位置最早的一个所在的位置为pos。

根据定义,它后面不应该再有其它种类的1号球了,但是直接求解很难,我们来考虑容斥。

设f(x)是恰好有x种球的1号球在pos后面。那么我们需要求解的就是f(0)。因为我们已经说了它是最后一个。

直接求解不行,那么就求解「至少」。设g(x)是至少有x种球的1号球在pos后面。

接下来%%%LNC的容斥严谨证明。

这里x其实不应该是一个变量来表示几种,而应该是一个集合来表示具体哪几种。

因为对于不同的种类,虽然它们的大小(种类数)相同,但是它们的转移值不同,所以只说集合大小是不严谨的。

根据定义,有$g(x) = \sum\limits_{x \subseteq y} f(y)$

根据子集反演,$f(x)=\sum\limits_{x \subseteq y} g(y) \times (-1)^{|y|-|x|}$

可以发现,这里的容斥系数只与大小有关,所以直接用大小转移而不加区分是正确的,但是并不代表可以直接那么推导。

所以说现在容斥系数也已经有了,问题就只剩下g的求解。

现在你已经枚举了last了,剩余的未知的就是pos了。于是我们也枚举它。

考虑总方案数:

首先,我们考虑钦定,在所有的last中选出一个放在pos位置上。答案乘a[last]

然后我们考虑1号求放在pos后面的那些种类的球,设它们一共有t个,那么它们任意排列的方案数为(t+a[last]-1)!。

之所以要减一,是因为你已经确定了last中的哪一个放在了pos上。

而pos后面的位置一共有m-pos个,在这些位置里你要挑出t+a[last]-1个来安排这些球,这是个组合数。

然后剩下的球还有(m-t-a[last])个。这个数的阶乘就是任意排列的方案。

但是这里有两处「任意排列」。其实并不能任意,因为你要求1号球必须在最前面了,所以还要除以$\prod\limits_{i \neq last}a_i$

全都乘起来,这就是总方案数。

可以发现,里面只有一项与pos有关,于是把它提出来单独求和,对于每个last计算一遍预处理出来。

然后就只与t有关了。你要知道它有几种方案,由几种球恰好凑出t个球。

这个就是一个经典的背包问题了,其中注意last是要必选的。求出这样的总方案数,然后还要容斥?

不需要。因为我们发现容斥的系数正负只与元素个数有关了,而元素个数恰好就是背包的物件数。

所以只要在背包转移时的+=全部转化为-=,这样系数就自动乘上了-1。(每多一个元素,容斥系数取相反数)

然后就没了。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define int long long
 4 int fac[1005],m,n,inv[1005],a[1005],cnt,t[55],v[55],mx,dp[1005],C[1005][1005],INV=1,ans,f[10005];
 5 #define mod 1000000007
 6 main(){
 7     scanf("%lld",&n);fac[0]=fac[1]=inv[1]=dp[0]=1;
 8     for(int i=1;i<=n;++i)scanf("%lld",&a[i]),m+=a[i],INV=INV*a[i]%mod;
 9     for(int i=2;i<=m;++i)fac[i]=fac[i-1]*i%mod,inv[i]=mod-mod/i*inv[mod%i]%mod;
10     for(int i=0;i<=m;++i)C[i][0]=1;
11     for(int i=1;i<=m;++i)for(int j=1;j<=i;++j)C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
12     for(int i=1;i<=m;++i)for(int j=i;j<=m;++j)f[i]=(f[i]+C[j-1][i-1])%mod;
13     for(int lst=1,tans;tans=a[lst],lst<=n;++lst){
14         for(int i=1;i<=m;++i)dp[i]=0;
15         for(int i=1;i<=n;++i)if(i!=lst)tans=tans*inv[a[i]]%mod;
16         for(int i=1;i<=n;++i)if(i!=lst)for(int j=m;~j;--j)dp[j+a[i]]=(dp[j+a[i]]-dp[j]+mod)%mod;
17         for(int t=0;t<=m;++t)ans=(ans+dp[t]*fac[t+a[lst]-1]%mod*fac[m-t-a[lst]]%mod*f[t+a[lst]]%mod*tans)%mod;
18     }cout<<ans<<endl;
19 }
View Code

猜你喜欢

转载自www.cnblogs.com/hzoi-DeepinC/p/12063627.html