2017/9/22模拟赛
题解:其实,我们可以把mod3余1、2、0的数分成3组,那么在mod3余1和2的集合中选出较大的,并在mod3余0的集合中随便加入一个数(如果有的话),这样就解决了任意2个数加起来不被3整除的条件了,对于不能被m个数整除的条件,跑一遍筛法,虽然复杂度很玄学,但是这样暴力能拿到很理想的分数--Jimmy。
代码如下:(暴力)
1 #include<cstdio> 2 #include<iostream> 3 #include<map> 4 using namespace std; 5 map<int,int> f; 6 int cnt[3],n,m,a[20]; 7 void prime(){ 8 for(int i=1;i<=m;i++) 9 for(int j=1;j*a[i]<=n;j++) 10 if(!f[a[i]*j]){ 11 f[a[i]*j]=1; 12 --cnt[(a[i]*j)%3]; 13 } 14 } 15 int main(){ 16 freopen("overweight.in","r",stdin); 17 freopen("overweight.out","w",stdout); 18 scanf("%d%d",&n,&m); 19 for(int i=1;i<=m;i++){ 20 scanf("%d",&a[i]); 21 if(a[i]==1){printf("0");return 0;} 22 } 23 cnt[0]=cnt[1]=cnt[2]=n/3; 24 if(n%3==1) ++cnt[1]; 25 if(n%3==2){++cnt[1];++cnt[2];} 26 prime(); 27 printf("%d",min(1,cnt[0])+max(cnt[1],cnt[2])); 28 return 0; 29 }
题解:这题是最大闭合子图,首先我们发现以下性质:①一个骑士移动后,横纵坐标之和的奇偶性会改变;②负权边可以直接扔掉,对答案不会影响。那么,我们就可以把s向所有奇数边连一条权值为x的边,所有偶数边向t连一条权值为x的边,所有奇数边能拓展到的非负权边连一条权值为INF的边,求它的最小割,用非负权边之和-最小割即为答案。
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #define MN 500005 6 #define INF 0x7fffffff 7 using namespace std; 8 int dx[8]={-2,-2,-1,-1,1,1,2,2},dy[8]={-1,1,-2,2,-2,2,-1,1}; 9 struct edge{int to,cap,next,rev;}e[MN]; 10 int s,t,m,n,cnt,head[MN],lev[MN],q[MN],sum; 11 void ins(int u,int v,int w){ 12 e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].cap=w;e[cnt].rev=cnt+1; 13 e[++cnt].to=u;e[cnt].next=head[v];head[v]=cnt;e[cnt].cap=0;e[cnt].rev=cnt-1; 14 } 15 bool bfs(){ 16 memset(lev,-1,sizeof(lev)); 17 int hd=0,tl=1; 18 lev[s]=0; q[hd]=s; 19 while(hd<tl){ 20 int v=q[hd++]; 21 for(int i=head[v];i;i=e[i].next) 22 if(e[i].cap>0&&lev[e[i].to]<0){ 23 lev[e[i].to]=lev[v]+1; 24 q[tl++]=e[i].to; 25 } 26 } 27 if(lev[t]==-1) return false; 28 return true; 29 } 30 int dfs(int u,int f){ 31 int used=0; 32 if(u==t) return f; 33 for(int i=head[u];i;i=e[i].next){ 34 if(e[i].cap>0&&lev[u]<lev[e[i].to]){ 35 int w=dfs(e[i].to,min(f-used,e[i].cap)); 36 if(w>0){ 37 e[i].cap-=w; e[e[i].rev].cap+=w; used+=w; 38 if(used==f) break; 39 } 40 } 41 } 42 if(!used) lev[u]=-1; 43 return used; 44 } 45 int dinic(){ 46 int flow=0; 47 while(bfs()) flow+=dfs(s,INF); 48 return flow; 49 } 50 bool check(int x,int y){return x>=1&&x<=n&&y>=1&&y<=m;} 51 int main() 52 { 53 freopen("sacrifice.in","r",stdin); 54 freopen("sacrifice.out","w",stdout); 55 scanf("%d%d",&n,&m); s=0; t=n*m+1; 56 for(int i=1;i<=n;i++) 57 for(int j=1;j<=m;j++){ 58 int x; scanf("%d",&x); 59 if(x<=0) continue;else sum+=x; 60 if(i+j&1) ins(s,(i-1)*m+j,x); 61 else ins((i-1)*m+j,t,x); 62 } 63 int ni,nj; 64 for(int i=1;i<=n;i++) 65 for(int j=1;j<=m;j++) 66 if(i+j&1)for(int k=0;k<8;k++) 67 if(check(ni=i+dx[k],nj=j+dy[k])) 68 ins((i-1)*m+j,(ni-1)*m+nj,INF); 69 printf("%d",sum-dinic()); 70 return 0; 71 }
操作声明:tp:枚举第i个点为终点时在起点等待的时间。x-i:竹笋长出来的时间-走到该点最少需要时间,即要采摘该点竹笋至少需要在起点等待的时间。
题解:若知道我们知道终点,那么最优的情况就是在起点等待若干秒,最后不停地走到终点且到终点时时间刚好结束,所以我们就枚举终点啦。用一个堆维护最大值,一旦堆顶大于tp,则弹出,ans减去该点权值。若权值为负,直接踢掉即可。
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #include<queue> 4 #define MN 500005 5 using namespace std; 6 int n,m,w[MN],tp; 7 long long ans,mx; 8 struct node{ 9 int pos,v; 10 friend bool operator<(const node& a,const node& b){ 11 return a.pos<b.pos; 12 } 13 }; 14 priority_queue<node> q; 15 int main() 16 { 17 freopen("urge.in","r",stdin); 18 freopen("urge.out","w",stdout); 19 scanf("%d%d",&n,&m); tp=m-1; 20 for(int i=1;i<=n;i++) scanf("%d",&w[i]); 21 for(int i=1;i<=min(n,m-1);i++){ 22 tp--; int x; 23 scanf("%d",&x); x-=i; 24 while(!q.empty()&&q.top().pos>tp) ans-=q.top().v,q.pop(); 25 if(x<=tp&&w[i]>0) q.push((node){x,w[i]}),ans+=w[i]; 26 mx=max(mx,ans); 27 } 28 printf("%lld",mx); 29 return 0; 30 }