NOIP2017提高组 模拟赛 26(总结)

来源:互联网 发布:9090端口 编辑:程序博客网 时间:2024/06/08 20:18

NOIP2017提高组 模拟赛 26(总结)

第一题 种花 (构造法/上下界的网络流)

【题目描述】

【解题思路】

  ①构造法:
  假设现在有m个点,删除一个点,剩下的m-1个点都已经确定,那么删除的这个点应该染什么颜色。
  假如这个点所在的行的点数为奇数,那么点的染色只与它所在的列有关(若列有偶数个点,那么这个点的颜色就可以确定了,哪种颜色少选哪种。否则随便选)
  假如这个点所在的列的点数为奇数,与上面类似。
  若不存在这样的点呢?
  那么剩下的一定是所有行和列的点数皆为偶数。
  那就随便选择一个点i。把它删掉。然后递归去求剩下m-1个点的情况。
  那么,点i所在的行和列的点数皆为奇数,假设现在的行(除去点i)的染色是红>白,点i一定是白色。除去这个行,剩下的所有的点的红白相等。除去这个列,红白也相等。所以现在的列(除去点i)的染色也必定是红>白。所以点i为白色是可行的。(若染色情况是白>红,结果也是一样的)
  递归求解即可,时间复杂度:O(n^2)。
  ②上下界的网络流。
  S与行连边,下界为行数/2,上界为(行数+1)/2。
  把每个点拆成一条边(ai,bi),下界为0,上界为1。(若有流量,则选红色)
  T与列连边,下界为列数/2,上界为(列数+1)/2。
  ai与点i所在的行连边,下界为0,上界为1。bi与点i所在的列连边,下界为0,上界为1。
  关于上下界网络流的构图解析

【代码】

//构造法#include<cstdio>#include<algorithm>#include<cstring>using namespace std;typedef long long ll;const int N=1500;struct data { int a,b; } d[N];int w[N],n,T,row[N],lie[N],vis[N];data ry[N],ly[N];bool cmp1(int a,int b) { return (d[a].a<d[b].a); }bool cmp2(int a,int b) { return (d[a].b<d[b].b); }void dfs(int x){    if(x>n) return;    bool ff=1; int yu=-1,ia,ib;    for(int i=1;i<=n;i++)    {        if(vis[i]) continue;        yu=i;        ia=d[i].a; ib=d[i].b;        if(row[ia]&1 || lie[ib]&1)        {            vis[i]=1;            row[ia]--; lie[ib]--;            dfs(x++);            row[ia]++; lie[ib]++;            if(!(row[ia]&1))            {                if(ry[ia].a<ry[ia].b) ry[ia].a++,ly[ib].a++,vis[i]=2;                else ry[ia].b++,ly[ib].b++,vis[i]=3;            } else            {                if(ly[ib].a<ly[ib].b) ry[ia].a++,ly[ib].a++,vis[i]=2;                else ry[ia].b++,ly[ib].b++,vis[i]=3;            }            ff=0;            break;        }    }    if(ff && yu!=-1)    {        ia=d[yu].a; ib=d[yu].b;        vis[yu]=1;        row[ia]--; lie[ib]--;        dfs(x++);        row[ia]++; lie[ib]++;        if(ly[ib].a<ly[ib].b) ry[ia].a++,ly[ib].a++,vis[yu]=2;        else ry[ia].b++,ly[ib].b++,vis[yu]=3;    }}int main(){    freopen("2084.in","r",stdin);    freopen("2084.out","w",stdout);    scanf("%d",&T);    while(T--)    {        scanf("%d",&n);        for(int i=1;i<=n;i++)        {            row[i]=lie[i]=vis[i]=0;            ry[i].a=ry[i].b=0;            ly[i].a=ly[i].b=0;        }        for(int i=1;i<=n;i++) scanf("%d%d",&d[i].a,&d[i].b),w[i]=i;        sort(w+1,w+1+n,cmp1);        int la=d[w[1]].a; d[w[1]].a=1; row[d[w[1]].a]++;        for(int i=2;i<=n;i++)        {            int v=w[i];            if(d[v].a==la) d[v].a=d[w[i-1]].a; else la=d[v].a,d[v].a=d[w[i-1]].a+1;            row[d[v].a]++;        }        sort(w+1,w+1+n,cmp2);            la=d[w[1]].b; d[w[1]].b=1; lie[d[w[1]].b]++;        for(int i=2;i<=n;i++)        {            int v=w[i];            if(d[v].b==la) d[v].b=d[w[i-1]].b; else la=d[v].b,d[v].b=d[w[i-1]].b+1;            lie[d[v].b]++;        }        dfs(1);        for(int i=1;i<=n;i++) printf("%c",(vis[i]==2)?('W'):('R'));        printf("\n");    }    return 0;}
//上下界网络流#include<cstdio>#include<algorithm>#include<cstring>#include<iostream>#define imin(a,b) (((a)<(b))?(a):(b))#define row(i) (i)#define ia(i) (n+i)#define ib(i) (n+n+i)#define lie(i) (n+n+n+i)using namespace std;typedef long long ll;const int inf=1e9;const int N=1500;struct data { int a,b; } d[N];int w[N],n,Tt,row[N],lie[N];int mh,ml,red[N];const int nn=1005000;int to[nn],h[nn],ne[nn],val[nn],tt;int lev[nn],vis[nn];int S,T,Sd,Td,ru[nn],bfstime;int q[nn],head,tail;bool cmp1(int a,int b) { return (d[a].a<d[b].a); }bool cmp2(int a,int b) { return (d[a].b<d[b].b); }void addedge(int a,int b,int c){    to[++tt]=b; ne[tt]=h[a]; val[tt]=c; h[a]=tt;    to[++tt]=a; ne[tt]=h[b]; val[tt]=0; h[b]=tt;}void ready(){    memset(vis,0,sizeof(vis));    memset(to,0,sizeof(to));    memset(ne,0,sizeof(ne));    memset(h,0,sizeof(h));    memset(lev,-1,sizeof(lev));    memset(ru,0,sizeof(ru));    memset(val,0,sizeof(val));    tt=1; head=tail=0;    S=lie(n)+10; T=S+1;    Sd=T+1; Td=Sd+1;    for(int i=1;i<=n;i++) row[i]=lie[i]=0;    bfstime=0;}bool bfs(){    head=tail=1;    q[head]=Sd; vis[Sd]=++bfstime; lev[Sd]=1;    while(head<=tail)    {        int u=q[head++];        for(int p=h[u];p;p=ne[p])        {            int v=to[p];            if(vis[v]==bfstime || !val[p]) continue;            q[++tail]=v;            vis[v]=bfstime; lev[v]=lev[u]+1;        }    }    return (vis[Td]==bfstime);}int dfs(int now,int maxf){    int ret=0,t;    if(now==Td || !maxf) return maxf;    for(int p=h[now];p;p=ne[p])    {        int v=to[p];        if(!val[p] || lev[v]!=lev[now]+1) continue;        t=dfs(v,imin(maxf,val[p]));        val[p]-=t;        val[p^1]+=t;        maxf-=t;        ret+=t;    }    if(maxf) lev[now]=-1;    return ret;}int dinic(){    int ret=0;    while(bfs()) ret+=dfs(Sd,inf);    return ret;}int main(){    freopen("2084.in","r",stdin);    freopen("2084.out","w",stdout);    scanf("%d",&Tt);    while(Tt--)    {        scanf("%d",&n); ready();        for(int i=1;i<=n;i++) scanf("%d%d",&d[i].a,&d[i].b),w[i]=i;        sort(w+1,w+1+n,cmp1);        int la=d[w[1]].a; d[w[1]].a=1; row[d[w[1]].a]++;        for(int i=2;i<=n;i++)        {            int v=w[i];            if(d[v].a==la) d[v].a=d[w[i-1]].a; else la=d[v].a,d[v].a=d[w[i-1]].a+1;            row[d[v].a]++;        } mh=d[w[n]].a;        sort(w+1,w+1+n,cmp2);            la=d[w[1]].b; d[w[1]].b=1; lie[d[w[1]].b]++;        for(int i=2;i<=n;i++)        {            int v=w[i];            if(d[v].b==la) d[v].b=d[w[i-1]].b; else la=d[v].b,d[v].b=d[w[i-1]].b+1;            lie[d[v].b]++;        } ml=d[w[n]].b;        for(int i=1;i<=mh;i++)        {            ru[S]-=row[i]>>1;            ru[row(i)]+=row[i]>>1;            if(row[i]&1) addedge(S,row(i),1);        }        for(int i=1;i<=ml;i++)        {            ru[T]+=lie[i]>>1;            ru[lie(i)]-=lie[i]>>1;            if(lie[i]&1) addedge(lie(i),T,1);        }        for(int i=1;i<=n;i++)        {            addedge(ia(i),ib(i),1);            red[i]=tt;            addedge(row(d[i].a),ia(i),1);            addedge(ib(i),lie(d[i].b),1);        }        if(ru[S]>0) addedge(Sd,S,ru[S]); else        if(ru[S]<0) addedge(S,Td,-ru[S]);        if(ru[T]>0) addedge(Sd,T,ru[T]); else        if(ru[T]<0) addedge(T,Td,-ru[T]);        for(int i=1;i<=mh;i++)        {            if(ru[row(i)]>0) addedge(Sd,row(i),ru[row(i)]); else            if(ru[row(i)]<0) addedge(row(i),Td,-ru[row(i)]);        }        for(int i=1;i<=ml;i++)        {            if(ru[lie(i)]>0) addedge(Sd,lie(i),ru[lie(i)]); else            if(ru[lie(i)]<0) addedge(lie(i),Td,-ru[lie(i)]);        }        addedge(T,S,inf);        int maxflow=dinic();        for(int i=1;i<=n;i++) printf("%c",(val[red[i]]?('W'):('R')));        printf("\n");    }    return 0;}

第二题 X之谜 (背包DP)

【题目描述】

【解题思路】

  ①首先确定的是,顺序是没关系的,只需求出来选数的方案数就成。因为选好了,一定可以按题意排好序的。
  ②把数按照模k的值分类。因为对答案有影响的也只是模数而已。
  ③背包DP。F[i][j][h]表示做到了模为i的数,已经选了j个数,模数为h的方案数。
  那么,假设在模为i的数中选了g个数,选法有w种。
  F[i][j+g][(h+g*i)%k]+=F[i-1][j][h]*w;
  那,w怎么求?
  假设,模为i的有s个数。
  那么,就相当于把g个小球放入s个桶的方案数。
  建立g个小球,再加入s-1个挡板。第i个挡板到第i-1个挡板之间的球就是桶i里的小球个数,第s个桶的小球数为剩下的小球数(第s-1个挡板之后的小球)
  只需求出放挡板的方案数,就是小球放入桶里的方案数。
  方案数=C(s+g-1,s-1),也就是C(s+g-1,g)
  ④因为C(n+m,n)=C(n+m,m),拓展证出,交换n,m的值,答案不变。
  算组合方案时,要用到逆元……

【代码】

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;typedef long long ll;const int mods=2017;int T,n,m,k;int c[55],cs[5];ll f[55][55][55],sq[5][55];ll Pow(ll x,ll y){    ll s=1;    for(;y;y>>=1)    {        if(y&1) s=s*x%mods;        x=x*x%mods;    }    return s;}int main(){    freopen("2085.in","r",stdin);    freopen("2085.out","w",stdout);    scanf("%d",&T);    while(T--)    {        scanf("%d%d%d",&n,&m,&k);        if(n<m) swap(n,m);        memset(f,0,sizeof(f));        for(int i=0;i<k;i++) c[i]=0;        int yc=n/k; cs[0]=yc; cs[1]=yc+1; yc=n%k;        for(int i=0;i<=yc;i++) c[i]=1;        for(int i=0;i<2;i++)        {            ll sd; int yr=cs[i];            for(int j=0;j<=m;j++)            {                sd=1ll;                for(int h=1;h<=j;h++)                sd=sd*(yr+j-h)%mods*Pow(h,mods-2)%mods;                sq[i][j]=sd;            }        }        for(int i=0;i<=m;i++) f[0][i][0]=sq[c[0]][i];        for(int i=0;i<k-1;i++)            for(int j=0;j<=m;j++)                for(int h=0;h<k;h++)                    if(f[i][j][h])                    {                        for(int co=0;co<=m-j;co++)                        {                            int yu=(h+(i+1)*co%k)%k;                            (f[i+1][j+co][yu]+=(f[i][j][h]*sq[c[i+1]][co])%mods)%=mods;                        }                    }        printf("%lld\n",f[k-1][m][0]);    }    return 0;}

第三题 大数开方 (模拟题)

【题目描述】

【解题思路】

   按照题意模拟即可。

【代码】

#include<cstdio>#include<algorithm>#include<cstring>int l,w;int ans[1500];int work(int o,char *O,int I){    char c,*D=O;    if(o>0)    {        for(l=0;D[l];D[l++]-=10)        {            D[l++]-=120;            D[l]-=110;            while(!work(0,O,l)) D[l]+=20;            ans[++w]=((D[l]+120-48)/20);        }    }    else    {        if(I>=0) c=o+(D[I]+130-48)%10-(I>l/2)*(D[I-l+I]+120-48)/10-9;        if(I>=0) D[I]+=!(o=work(c/10,O,I-1))*((c+999)%10-(D[I]+140-48)%10);    }    return o;}int main(){    freopen("2086.in","r",stdin);    freopen("2086.out","w",stdout);    char s[1200];s[0]='0';    scanf("%s",s+1);    int sl=strlen(s);    char sp[]="0000000000000000000000";    strcat(s,sp);    if(sl&1) work(2,s+1,0); else work(2,s,0);    if(ans[w]>4) ans[w-1]++;    for(int i=1;i<=w-11;i++) printf("%d",ans[i]);    printf(".");    for(int i=w-10;i<=w-1;i++) printf("%d",ans[i]);    printf("\n");    return 0;}
原创粉丝点击