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

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值