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&<%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; }