2017.9.22

超重(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^8,0<=m<=15。

【样例解释】

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

 

题解:(数据较水,用筛法暴力能打九十分。。。)利用容斥原理,将m个数的所有乘积表示出来,统计用了几个数字,偶数个加奇数个减就可以了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int n,m,ans,sum;
int a[20];
bool fg;
void cal(int md,int pro,int tg){
    if(pro%3==0) return;
    int x1,y1;
    if(pro%3==1) y1=3-md;else y1=md;
    x1=pro*y1+md;
    if(x1>n+md) return;
    ans+=tg*((n+md-x1)/(3*pro)+1);
}
int gcd(int x,int y){return y?gcd(y,x%y):x;}
ll lcm(int x,int y){int z=gcd(x,y);return 1LL*x*y/z;}
void dfs(int x,int md,int pro,int tg){
    if(x>m){cal(md,pro,tg); return;}
    ll lt=lcm(pro,a[x]);
    if(lt<=n&&lt%3) dfs(x+1,md,lt,-tg);
    dfs(x+1,md,pro,tg);
}
int main(){
    freopen("overweight.in","r",stdin);
    freopen("overweight.out","w",stdout);
    int i;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;++i) scanf("%d",&a[i]),fg|=a[i]==3;
    sort(a+1,a+m+1);
    m=unique(a+1,a+m+1)-a-1;
    if(a[1]==1) return 0*printf("0");
    ans=0;dfs(1,1,1,1);sum=ans;
    ans=0;dfs(1,2,1,1);sum=max(sum,ans);
    if (!fg&&n>=3) ++sum;
    printf("%d",sum);
}

 

献祭(sacrifice)

【题目描述】

小C喜欢在他的n*m的棋盘上摆弄他的骑士们,每个格子上都有一个骑士,每个骑士有一个XY值vij。现在有人想掀翻他的棋盘用以献祭,小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<=20000,n*m<=20000,|vij|<=20000。

【样例解释】

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

 

题解:是最大权闭合子图问题,判断它是否是个二分图,由于每次跳动x+y的奇偶性都会改变,所以以奇偶连线,网络流。对于负权,不选它肯定更优,所以我们直接把它扔掉。因为每个格子最多连出8条边,所以即使点数有20000,边数仍非常少,用Dinic可以极其快速地通过该题。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MN 30005
#define MM 500005
#define INF 0x3FFFFFFF
using namespace std;
const int fx[8][2]={{-2,-1},{-2,1},{-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}};
struct edge{int nex,to,flw;}e[MM];
int hr[MN],d[MN],dep[MN],q[MN];
int S,T,pin,hd,tl,n,m,sum;
void ins(int x,int y,int z){
    e[++pin]=(edge){hr[x],y,z}; hr[x]=pin;
    e[++pin]=(edge){hr[y],x,0}; hr[y]=pin;
}
int dfs(int x,int fw){
    if(x==T) return fw;
    int sum=0,lt;
    for(int& i=d[x];i;i=e[i].nex){
        if(dep[x]+1!=dep[e[i].to]||!e[i].flw) continue;
        lt=dfs(e[i].to,min(fw,e[i].flw));
        sum+=lt; fw-=lt;
        e[i^1].flw+=lt; e[i].flw-=lt;
        if(!fw) return sum;
    }
    dep[x]=0;
    return sum;
}
bool bfs(){
    int i,x;
    memset(dep,0,sizeof(dep));
    for(dep[q[hd=tl=1]=S]=1;hd<=tl;++hd)
        for(x=q[hd],i=d[x]=hr[x];i;i=e[i].nex)
            if(!dep[e[i].to]&&e[i].flw) dep[e[i].to]=dep[x]+1,q[++tl]=e[i].to;
    return dep[T];
}
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);
    register int i,j,k,x,ni,nj;
    scanf("%d%d",&n,&m);
    S=0;T=n*m+1;pin=1;sum=0;
    for(i=1;i<=n;++i)
        for(j=1;j<=m;++j){
            scanf("%d",&x);
            if(x<=0) continue;else sum+=x;
            if(i+j&1) ins(S,(i-1)*m+j,x);else ins((i-1)*m+j,T,x);
        }
    for(i=1;i<=n;++i)
        for(j=1;j<=m;++j) if(i+j&1)
            for(k=0;k<8;++k) if(check(ni=i+fx[k][0],nj=j+fx[k][1]))
                ins((i-1)*m+j,(ni-1)*m+nj,INF);
    while(bfs()) sum-=dfs(S,INF);
    printf("%d",sum);
}

 

 

欲望(urge)

【题目描述】

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

【输入数据】

第一行两个正整数n,T。

第二行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^5,0<=T , ti , |ai|<=10^9。

【样例解释】

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

 

题解:若知道我们知道终点,那么最优的情况就是在起点等待若干秒,最后不停地走到终点且时间刚好结束,所以我们就枚举终点。用一个堆维护最大值,一旦堆顶大于tp,则弹出,ans减去该点权值。若权值为负,直接踢掉即可。

#include<cstdio>
#include<iostream>
#include<queue>
#define MN 500005
using namespace std;
int n,m,w[MN],tp;
long long ans,mx;
struct node{
    int pos,v;
    friend bool operator<(const node& a,const node& b){
        return a.pos<b.pos;
    }
};
priority_queue<node> q;
int main()
{
    freopen("urge.in","r",stdin);
    freopen("urge.out","w",stdout);
    scanf("%d%d",&n,&m); tp=m-1; 
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    for(int i=1;i<=min(n,m-1);i++){
        tp--; int x;
        scanf("%d",&x); x-=i;
        while(!q.empty()&&q.top().pos>tp) ans-=q.top().v,q.pop();
        if(x<=tp&&w[i]>0) q.push((node){x,w[i]}),ans+=w[i];
        mx=max(mx,ans);
    }
    printf("%lld",mx);
    return 0;
}

 

posted @ 2017-09-25 17:35  Vanity  阅读(142)  评论(0)    收藏  举报