zoukankan      html  css  js  c++  java
  • [考试反思]1218省选模拟2: 告诫

    CCF NOI 2018温馨提示:

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

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

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

    两个打IOI赛制的得分是170/140。(fixed,kx大神怎么可能只有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
    ",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:圈地游戏

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

    更了,去模拟测试24看吧。

    T3:组合数学

    题目大意:给定数列a,对于其所有排列b求前缀和得到s,求$sumlimits_{b=permutation(a)} frac{m!}{prodlimits_{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) = sumlimits_{x subseteq y} f(y)$

    根据子集反演,$f(x)=sumlimits_{x subseteq y} g(y) imes (-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号球必须在最前面了,所以还要除以$prodlimits_{i eq 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
  • 相关阅读:
    MySQL无法登录服务器解决方法
    photoshop mac版下载及破解
    静态html传参数
    flash与php 交互(as传参给php)
    PHP发送邮件类库PHPMailer的简单使用
    PHP CodeBase: 判断用户是否手机访问
    HTTP报文
    有关phpmailer的详细介绍及使用方法
    JS 实现 Tab标签切换功能
    new Option()——实现时间联动
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12063627.html
Copyright © 2011-2022 走看看