校内模拟赛(20170922)

Posted ghostfly233

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了校内模拟赛(20170922)相关的知识,希望对你有一定的参考价值。

学长终于良心出题了!

给学长打call!

————————————————我是分割线——————————————————

T1:超重(overweight

【题目描述】

一天小D正在开飞机,当他吃完午饭,机舱里突然响起了警报声,原来是飞机超重了。小D当机立断,开始将机舱里的一些物品扔出窗外。小D的飞机里有n个物品,XY值分别为1~n,他希望扔掉尽量多的东西。但小D持有的两种强迫症让他迟迟无法下手:第一种是他希望所有扔掉的东西中,任意两个东西的XY值之和不是3的倍数;第二种是他有m个数字a1~am任意一个扔掉的东西的XY值都不能被这m个数字中的任意一个整除。所以他把这个任务交给了担任僚机的你。

 

【输入数据】

第一行两个正整数n,m

第二行m个数,表示小D拥有的数字。

【输出数据】

输出一个整数,表示小D最多能扔掉多少物品。

【样例输入】

4 1

1

【样例输出】

    0

 

【数据范围】

对于10%的数据,n<=15

对于30%的数据,n<=5*10^6

另外10%的数据,m=0

另外10%的数据,ai<m

对于100%的数据,1<=n , ai<=5*10^80<=m<=15

【样例解释】

    因为1整除任何正整数,所以小D不能扔掉任何东西。

————————————————我是分割线——————————————————

显然我们只能取全部都是%3=1的数或者全部都是%3=2的数,如果有%3=0的数我们还能对答案+1

难度在与m的倍数不能取。

由于我们看到m非常的小,我们考虑容斥原理。

首先预处理出f[0],f[1],f[2]一开始有多少个,

然后我们直接暴力容斥就好啦!

下面贴代码

#include<cstdio>
#define max(a,b) ((a)>(b)?(a):(b))
#define MN 16
#define ul unsigned long long 
using namespace std;
int a[MN],qaq,f[3],n,m;
ul res=0;
long long chu(long long aa,int bb){return aa==0?aa:aa/bb;}
long long gcd(long long aa,int bb){return bb==0?aa:gcd(bb,aa%bb);}
long long lcm(long long aa,int bb){return aa*bb/gcd(aa,bb);}
void dfs(ul res,int now,int sum,int goal,int cz){
    if(sum==goal){
        int qaq=res%3,tmp=chu(n/res,3);
        if(qaq==2){
            f[0]+=cz*tmp;
            f[1]+=cz*(tmp+(n/res-tmp*3==2));
            f[2]+=cz*(tmp+(n/res-tmp*3>=1));
        }else if(qaq==1){
            f[0]+=cz*tmp;
            f[1]+=cz*(tmp+(n/res-tmp*3>=1));
            f[2]+=cz*(tmp+(n/res-tmp*3==2));
        }else f[0]+=cz*(n/res);
        return;
    }else if(now==m+1)return;
    for(int i=now;i<=m;i++){
        long long qqq=lcm(res,a[i]);
        if(qqq<=n)dfs(qqq,i+1,sum+1,goal,cz);
    }
}
int main(){
    freopen("overweight.in","r",stdin);
    freopen("overweight.out","w",stdout);
    scanf("%d%d",&n,&m);f[0]=n/3,f[1]=n/3+(n-n/3*3>=1),f[2]=n/3+(n-n/3*3==2);
    for(int i=1;i<=m;i++)scanf("%d",&a[i]);
    for(int i=1;i<=m;i++)
        dfs(1,1,0,i,i%2?-1:1);
    printf("%d\\n",max(f[1],f[2])+((f[0]>0)?1:0));
    fclose(stdin);
    fclose(stdout);
}

————————————————我是分割线——————————————————

T2:献祭(sacrifice

【题目描述】

C喜欢在他的n*m的棋盘上摆弄他的骑士们,每个格子上都有一个骑士,每个骑士有一个XYvij。现在有人想掀翻他的棋盘用以献祭,小C当然不能苟同,他决定通过摆阵来吓傻对方。他希望留下若干个骑士,使得它们的XY值总和最大。但是处于阵法内的骑士会被施加一些没用的BUFF,每个骑士会攻击它们的控制区域。所以小C希望处于阵法中所有骑士互不攻击。

(一个骑士的控制区域为国际象棋中的“马”从该位置出发走一步能到的位置,如图所示:)

 

【输入数据】

第一行两个正整数n,m

    接下来n行,每行m个整数,表示每个格子上的骑士的XY值。

【输出数据】

输出一个整数,表示最终阵法最大的XY值总和。

【样例输入】

3 3

1 0 1

2 1 1

0 1 1

【样例输出】

5

【数据范围】

对于10%的数据,n*m<=15

对于30%的数据,n*m<=300

另外20%的数据,vij>0

对于100%的数据,1<=n , m<=20000n*m<=20000|vij|<=20000

【样例解释】

选取(2,1)(2,2)(2,3)(3,2)四个格子上的骑士,2+1+1+1=5

————————————————我是分割线——————————————————

黑白染色网络流,不解释。

 

#include<cstdio>
#include<cstring>
#define inf 0x3f3f3f3f
#define min(a,b) ((a)<(b)?(a):(b))
#define MN 20005
using namespace std;
int a[MN],level[MN],iter[MN],head[MN],num=1,n,m,que[MN],S,T,sum,tot,nx,ny;
const int fx[8][2]={{-2,-1},{-2,1},{-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}};
struct edge{
    int to,next,w;
}g[MN<<6];
bool spfa(){
    memset(level,0,sizeof(level));
    memcpy(iter,head,sizeof(head));
    int h=1,t=1;que[1]=S;level[S]=1;
    while(h<=t){
        int tmp=que[h++];
        for(int i=head[tmp];i;i=g[i].next)
            if(level[g[i].to]==0&&g[i].w)level[g[i].to]=level[tmp]+1,que[++t]=g[i].to;
    }return level[T]!=0; 
}
int dfs(int u,int flow){
    if(u==T)return flow;
    int used=0;
    for(int &i=iter[u];i;i=g[i].next)
        if(level[g[i].to]==level[u]+1&&g[i].w){
            int qaq=dfs(g[i].to,min(g[i].w,flow-used));
            if(qaq){
                g[i].w-=qaq,g[i^1].w+=qaq,used+=qaq;
                if(used==flow)return flow;
            }
        }return used;
}
int dinic(){
    int tot=0,now;
    while(spfa())do now=dfs(S,inf),tot+=now;while(now);
    return tot;
}
void ins(int u,int v,int w){g[++num].next=head[u];head[u]=num;g[num].to=v;g[num].w=w;}
void insw(int u,int v,int w){ins(u,v,w);ins(v,u,0);}
void insw2(int u,int v){ins(u,v,inf);ins(v,u,inf);}
inline bool check(int x,int y) {return x>=1&&x<=n&&y>=1&&y<=m;}
int main(){
    freopen("sacrifice.in","r",stdin);
    freopen("sacrifice.out","w",stdout);
    scanf("%d%d",&n,&m);S=0,T=n*m+1;
    for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a[(i-1)*m+j]),sum+=a[(i-1)*m+j]>0?a[(i-1)*m+j]:0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(a[(i-1)*m+j]>0)
                if(tot%2)insw(S,(i-1)*m+j,a[(i-1)*m+j]);
                else insw((i-1)*m+j,T,a[(i-1)*m+j]);
            tot++;
        }
        if(m%2==0)tot++;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)if(i+j&1){
                    for(int k=1;k<=8;k++)if(check(nx=i+fx[k][0],ny=j+fx[k][1]))insw2((i-1)*m+j,(nx-1)*m+ny);
                }
    printf("%d\\n",sum-dinic());
    fclose(stdin);
    fclose(stdout);
}

 

————————————————我是分割线——————————————————

T3:欲望(urge

【题目描述】

H养了n棵竹笋,每棵竹笋有一个XYain棵竹笋整齐地排在他家门口的盐地里。冬去春来,又到了万物发春的季节。这一天,小H被一群人逼着去传火,但小H不同意,就开始了逃亡的旅途。他跑到家门口,突然想采集一下门口的竹笋,于是他开始朝盐地走去。由于竹笋们很傲娇,第i棵竹笋会在ti时刻钻出地面,此前它都会一直藏于地下。小H0时刻站在第1棵竹笋的左边,即0号位置。在每一时刻开始时他都会检测自己脚下是否有钻出地面的竹笋,如果有,则决定是否采摘该竹笋,检测、采摘不需要花费时间;接下来他要对自己下一秒的行动作出决定,他可以决定自己用这一秒的时间移动到下一棵竹笋的位置,或是原地不动,但小H不能往回走。小HT时间在盐地进行如上操作,他希望最后采摘的竹笋的XY值总和最大。

 

【输入数据】

第一行两个正整数nT

第二行n个正整数ai,表示每棵竹笋的XY值。

第三行n个正整数ti,表示每棵竹笋何时会钻出地面。

【输出数据】

输出一个整数,表示最大的XY值总和。

【样例输入】

2 3

1 1

2 2

【样例输出】

    1

 

【数据范围】

对于5%的数据,ti=0

对于15%的数据,n , T<=100

对于30%的数据,n , T<=10000

另外20%的数据,ai=1

对于100%的数据,1<=n<=5*10^50<=T , ti , |ai|<=10^9

【样例解释】

H只能采到第1个或第2个竹笋。

 

以上是关于校内模拟赛(20170922)的主要内容,如果未能解决你的问题,请参考以下文章

20170922-3 功能测试作业

校内模拟赛T1大美江湖

校内模拟赛 虫洞(by NiroBC)

2017-9-3 校内模拟T2取数win

2017.6.11 校内模拟赛

校内模拟赛20170604