【20170923校内模拟赛】

所有题目开启-O2优化,评测机效率为4亿左右。


T1 超重 (overweight)

Description

一天小D正在开飞机,当他吃完午饭,机舱里突然响起了警报声,原来是飞机超重了。小D当机立断,开始将机舱里的一些物品扔出窗外。小D的飞机里有n个物品,XY值分别为1n**,他希望扔掉尽量多的东西。但小D持有的两种强迫症让他迟迟无法下手:第一种是他希望所有扔掉的东西中,**任意两个东西的XY值之和不是3的倍数**;第二种是他有m个数字a1am,任意一个扔掉的东西的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;
}
posted @ 2017-09-23 11:02  Melacau  阅读(190)  评论(0编辑  收藏  举报