所有题目开启-O2优化,评测机效率为4亿左右。
T1 超重 (overweight)
Description
一天小D正在开飞机,当他吃完午饭,机舱里突然响起了警报声,原来是飞机超重了。小D当机立断,开始将机舱里的一些物品扔出窗外。小D的飞机里有n个物品,XY值分别为1~n,他希望扔掉尽量多的东西。但小D持有的两种强迫症让他迟迟无法下手:第一种是他希望所有扔掉的东西中,任意两个东西的XY值之和不是3的倍数;第二种是他有m个数字a1~am,任意一个扔掉的东西的XY值都不能被这m个数字中的任意一个整除。所以他把这个任务交给了担任僚机的你。
Input
第一行两个正整数n,m。
第二行m个数,表示小D拥有的数字。
Output
输出一个整数,表示小D最多能扔掉多少物品。
Sample Input
4 1
1
Sample Output
0
Hint
对于10%的数据,\(n \leq 15\);
对于30%的数据,\(n \leq 5*10^6\);
另外10%的数据,\(m=0\);
另外10%的数据,\(ai<m\);
对于100%的数据,\(,1 \leq n , ai \leq 5*10^8,0 \leq m \leq 15\)。样例解释
因为1整除任何正整数,所以小D不能扔掉任何东西。
Solution
考虑m=1的情况,显然可以利用解同余方程的方式计算得出[1,n]中数mod 3 = 0,1,2 在去除a1的倍数的情况下的个数。
显然,当m超过1时,可以很简单的利用容斥原理得出结论,时间复杂度为\(O(m*m^2 \log n)\) (log为计算lcm的效率)
答案为\(max\{f[1],f[2]\}+(bool)(f[0])\)
Code
#include <stdio.h>
#define MN 20
#define R register
#define Filename "overweight"
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
inline int read(){
R int x; R bool f; R char c;
for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
return f?-x:x;
}
int a[MN],n,m;bool used[MN];ll f[3];
inline ll lcm(ll x,ll y){
R ll xx=x,yy=y,r;
while (y){
r=x%y;x=y,y=r;
}return xx*yy/x;
}
inline void dfs(ll res,int k,int to,int lst,int opt){
if (k>to){
R int tmp=n/res;if (res%3){
if (res%3==1){
f[1]+=opt*(tmp/3+(tmp%3?1:0));
f[2]+=opt*(tmp/3+(tmp%3>1?1:0));
f[0]+=opt*(tmp/3);
}else{
f[2]+=opt*(tmp/3+(tmp%3?1:0));
f[1]+=opt*(tmp/3+(tmp%3>1?1:0));
f[0]+=opt*(tmp/3);
}
}else f[0]+=opt*tmp;
return;
}if (lst+(to-k)+1>m) return;
for (R int i=lst+1; i<=m; ++i) if (!used[i]&&lcm(res,a[i])<=n)
{used[i]=1;dfs(lcm(res,a[i]),k+1,to,i,opt);used[i]=0;}
}
int main(){
#ifndef Debug
freopen(Filename".in","r",stdin);
freopen(Filename".out","w",stdout);
#endif
n=read(),m=read();f[0]=n/3,f[1]=n/3+(n%3?1:0),f[2]=n/3+(n%3>1?1:0);
for (R int i=1; i<=m; ++i) a[i]=read();for (R int i=1; i<=m; ++i) dfs(1,1,i,0,(i%2?-1:1));
printf("%lld\n",max(f[1],f[2])+((bool)f[0]));
#ifndef Debug
fclose(stdin); fclose(stdout);
#endif
return 0;
}
T2 献祭(sacrifice)
Description
小C喜欢在他的n*m的棋盘上摆弄他的骑士们,每个格子上都有一个骑士,每个骑士有一个XY值vij。现在有人想掀翻他的棋盘用以献祭,小C当然不能苟同,他决定通过摆阵来吓傻对方。他希望留下若干个骑士,使得它们的XY值总和最大。但是处于阵法内的骑士会被施加一些没用的BUFF,每个骑士会攻击它们的控制区域。所以小C希望处于阵法中所有骑士互不攻击。
(一个骑士的控制区域为国际象棋中的“马”从该位置出发走一步能到的位置,如图所示:)
Input
第一行两个正整数n,m。
接下来n行,每行m个整数,表示每个格子上的骑士的XY值。
Output
输出一个整数,表示最终阵法最大的XY值总和。
Sample Input
3 3
1 0 1
2 1 1
0 1 1
Sample Output
5
Hint
对于10%的数据,\(n*m \leq 15\);
对于30%的数据,$ n*m \leq 300\(; 另外20%的数据,\) vij>0$;
对于100%的数据,\(1 \leq n, m \leq 20000 , n*m \leq 2000 , |vij| \leq 20000\)。样例解释
选取(2,1)、(2,2)、(2,3)、(3,2)四个格子上的骑士,2+1+1+1=5。
Solution
典型的最大权闭合子图问题,发现马步满足奇偶性不同,于是进行黑白染色之后建图即可。
由于是二分图,强上Dinic即可,时间效率为$O(8 (nm)^\frac{3}{2}) $
Code
#include <stdio.h>
#include <string.h>
#define R register
#define MN 20005
#define ME 200005
#define S 0
#define T 20001
#define inf 0x3f3f3f3f
#define min(a,b) ((a)<(b)?(a):(b))
#define Filename "sacrifice"
inline int read(){
R int x; R bool f; R char c;
for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
return f?-x:x;
}
const int fx[8][2]={{-2,-1},{-2,1},{-1,-2},{-1,2},{1,-2},{1,2},{2,-1},{2,1}};
int n,m,sum,to[ME],nxt[ME],head[MN],cap[ME],que[MN],lev[MN],iter[MN],cnt=1;bool used[MN];
inline void ins(int x,int y,int v){to[++cnt]=y,nxt[cnt]=head[x],cap[cnt]=v,head[x]=cnt;}
inline void insw(int x,int y){ins(x,y,inf);ins(y,x,inf);}
inline void insw(int x,int y,int v){ins(x,y,v);ins(y,x,0);}
inline bool bfs(){
memset(lev,-1,sizeof(lev));
R int h=0,t=1;que[1]=S;lev[S]=0;
while(h<t){
R int u=que[++h];
for (R int i=head[u]; i; i=nxt[i])
if (cap[i]&&lev[to[i]]==-1){
lev[to[i]]=lev[u]+1;
que[++t]=to[i];
}
}
memcpy(iter,head,sizeof(head));
return lev[T]!=-1;
}
inline int dfs(int u,int f){
if (u==T) return f;
R int used=0;for (R int &i=iter[u]; i; i=nxt[i])
if (lev[to[i]]==lev[u]+1&&cap[i]){
R int w=dfs(to[i],min(f-used,cap[i]));
used+=w;cap[i]-=w;cap[i^1]+=w;
if (used==f) return f;
}
if (!used) lev[u]=-1;
return used;
}
inline int dinic(){
R int flow=0,tmp;
while(bfs())do flow+=(tmp=dfs(S,inf)); while(tmp);
return flow;
}
inline bool check(int x,int y) {return x>=1&&x<=n&&y>=1&&y<=m;}
int main(){
#ifndef Debug
freopen(Filename".in","r",stdin);
freopen(Filename".out","w",stdout);
#endif
n=read(),m=read();
for (R int i=1; i<=n; ++i)
for (R int j=1; j<=m; ++j){
R int x=read();
if (x<=0) continue; else sum+=x;
if (i+j&1) insw(S,(i-1)*m+j,x); else insw((i-1)*m+j,T,x);
}
for (R int i=1; i<=n; ++i)
for (R int j=1; j<=m; ++j) if (i+j&1)
for (R int k=0,ni,nj; k<8; ++k) if (check(ni=i+fx[k][0],nj=j+fx[k][1]))
insw((i-1)*m+j,(ni-1)*m+nj,inf);
printf("%d\n",sum-dinic());
#ifndef Debug
fclose(stdin); fclose(stdout);
#endif
return 0;
}
T3 欲望(urge)
Description
小H养了n棵竹笋,每棵竹笋有一个XY值ai。n棵竹笋整齐地排在他家门口的盐地里。冬去春来,又到了万物发春的季节。这一天,小H被一群人逼着去传火,但小H不同意,就开始了逃亡的旅途。他跑到家门口,突然想采集一下门口的竹笋,于是他开始朝盐地走去。由于竹笋们很傲娇,第i棵竹笋会在ti时刻钻出地面,此前它都会一直藏于地下。小H于0时刻站在第1棵竹笋的左边,即0号位置。在每一时刻开始时他都会检测自己脚下是否有钻出地面的竹笋,如果有,则决定是否采摘该竹笋,检测、采摘不需要花费时间;接下来他要对自己下一秒的行动作出决定,他可以决定自己用这一秒的时间移动到下一棵竹笋的位置,或是原地不动,但小H不能往回走。小H有T秒时间在盐地进行如上操作,他希望最后采摘的竹笋的XY值总和最大。
Input
第一行两个正整数n,T。
第二行n个正整数ai,表示每棵竹笋的XY值。
第三行n个正整数ti,表示每棵竹笋何时会钻出地面。
Output
输出一个整数,表示最大的XY值总和。
Sample Input
2 3
1 1
2 2
Sample Output
1
Hint
对于5%的数据,\(ti=0\);
对于15%的数据,\(n , T \leq 100\);
对于30%的数据,\(n , T \leq 10000\);
另外20%的数据,\(ai=1\);
对于100%的数据,\(1 \leq n \leq 5*10^5 , 0 \leq T , ti , |ai| \leq10^9\)。样例解释
小H只能采到第1个或第2个竹笋。
Solution
先从ai=1的情况入手: 假设你一直往前走不作停留,那么如果走到一个可采竹笋的地方,那么采下它一定更优。
所以我们枚举小H在哪个地方刚好(因时间不够而)停下来,因为竹笋不消失,可以证明使得地面上能够出现的竹笋最多的策略一定是在0号点等上若干秒,然后一直往前走,最后因为时间不够,恰好在我们枚举的地方停下来。
然后我们发现,枚举一个终点x后,我们在1~x可以采摘的竹笋的ti一定满足小于一个等差数列.
经过各种转化,我们发现随着枚举的终点位置x不断增加(从1~n),可以采摘的最大时刻L是不断降低的,如果转化后的ti(1<=i<=x)低于L,那么这些一定可以被选。
所以我们从1~n一边枚举终点一边做,开一个堆来维护(最大值),一旦堆顶大于L,那么把堆顶弹出,答案减去堆顶的wi值;如果当前枚举的终点小等于L,那么把它插入堆,并加入答案。
权值有负数的情况,不取肯定更优,我们直接把它扔掉即可。所以 时间复杂度为\(O(nlogn)\).
Code
#include <stdio.h>
#define MN 500005
#define R register
#define Filename "urge"
#define ll long long
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
inline int read(){
R int x; R bool f; R char c;
for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
return f?-x:x;
}
struct node{
int pos,val;
inline bool operator >(const node &b)const{
return pos>b.pos;
}
};
class heap{
private:
node hp[MN<<1];int sz;
inline void swap(node &a,node &b){R node t=b; b=a; a=t;}
public:
inline void clear(){sz=0;}
inline node get(){
R node tmp=hp[1];hp[1]=hp[sz--];
R int k=1;while((k<<1)<sz&&(hp[k<<1]>hp[k]||hp[k<<1|1]>hp[k]))
if (hp[k<<1]>hp[k<<1|1]){
swap(hp[k<<1],hp[k]);
k<<=1;
}else{
swap(hp[k<<1|1],hp[k]);k=k<<1|1;
}
if ((k<<1)==sz&&hp[k<<1]>hp[k]) swap(hp[k<<1],hp[k]);
return tmp;
}
inline void in(node x){
R int k=++sz;hp[k]=x;
while(k>1&&hp[k]>hp[k>>1]) swap(hp[k],hp[k>>1]),k>>=1;
}
inline node top(){return hp[1];}
inline bool empty(){return !sz;}
}heap;
int n,m,val[MN];ll ans,res;
int main(){
#ifndef Debug
freopen(Filename".in","r",stdin);
freopen(Filename".out","w",stdout);
#endif
n=read(),m=read();
for (R int i=1; i<=n; ++i) val[i]=read();
for (R int i=1,top=m-1; i<=min(n,m-1); ++i){
--top; R int x=read()-i;
while(!heap.empty()&&heap.top().pos>top) res-=heap.get().val;
if (x<=top&&val[i]>0) heap.in((node){x,val[i]}),res+=val[i];
ans=max(ans,res);
}printf("%lld\n",ans);
#ifndef Debug
fclose(stdin); fclose(stdout);
#endif
return 0;
}