2017-3-23校内训练
学长日常出丧题虐人 100+90+64=254/300
T1.数据结构
题目大意:给定n个数,每次给出x和y,询问一个区间内多少数模x等y。(n,询问次数,数字大小<=40000)
思路:用差分或者说是前缀和的思想,离线处理每个询问,我们从左到右把数字加入某种能查询模x等y的数据结构中,碰到询问区间左端点时让该询问的答案减去当前数据结构中模x等y的个数,碰到右端点时再加上这个个数。那么如何维护这个数据结构呢?可以分类讨论,对于x<=200,我们用数组f1[i][j]存下模i等j的值的个数,每次O(200)暴力修改,O(1)查询,对于x>200,我们用f2[i]存下值为i的个数,每次O(1)修改,查询的时候我们枚举y,x+y,2x+y……复杂度最大为O(40000/200=200)。总复杂度O(200(n+q))。
#include<cstdio> #include<algorithm> using namespace std; inline int read() { int x;char c; while((c=getchar())<'0'||c>'9'); for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0'; return x; } #define MN 40000 #define K 200 struct query{int p,x,y,i,f;}p[MN*2+5]; bool cmp(query a,query b){return a.p==b.p?a.f<b.f:a.p<b.p;} int a[MN+5],ans[MN+5],f[K+5][K+5],ff[MN+5]; int cal(int x,int y) { if(x<=K)return f[x][y]; int i,r=0; for(i=y;i<=MN;i+=x)r+=ff[i]; return r; } int main() { freopen("datastructure.in","r",stdin); freopen("datastructure.out","w",stdout); int n,q,i,j,k,l,r,x,y; n=read();q=read(); for(i=1;i<=n;++i)a[i]=read(); for(i=0;i<q;++i) { l=read();r=read();x=read();y=read(); p[i<<1]=(query){l,x,y,i,-1}; p[i<<1|1]=(query){r,x,y,i,1}; } sort(p,p+(q<<1),cmp); for(i=1,j=0;i<=n;++i) { for(;p[j].p==i&&p[j].f<0;++j)ans[p[j].i]-=cal(p[j].x,p[j].y); for(k=1;k<=K;++k)++f[k][a[i]%k];++ff[a[i]]; for(;p[j].p==i;++j)ans[p[j].i]+=cal(p[j].x,p[j].y); } for(i=0;i<q;++i)printf("%d\n",ans[i]); fclose(stdin);fclose(stdout);return 0; }
T2.刷水
题目大意:n道题,第i道在xi时出现,每道题开始难度为0,之后每秒增加wi,总共可以做k次题,问做完所有题的最小难度和。(n,k<=5000)
思路:容易想到用f[i][j]表示做i次做完前j道的最小难度和,则f[i][j]=min(f[i-1][p]+Σwl*(xj-xl)) (p<l<j),随便斜率优化就可以了,复杂度O(nk),我突然脑抽没想出来,打了决策单调性,多个log,本机测极限数据2.3s,时限3s,就放心交了,结果因为迷之原因被卡常了两个点(评测机配置与本机相同),拿到包自己测了下A了,玄学。
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define ll long long inline int read() { int x;char c; while((c=getchar())<'0'||c>'9'); for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0'; return x; } #define MN 5000 #define INF (1LL<<60) ll f[MN+5][MN+5]; int x[MN+5],w[MN+5],q[MN+5],ql,qr,t[MN+5]; int cal(int k,int n,int x,int y) { int l=x+1,r=n,mid,res=n+1; while(l<=r) { mid=l+r>>1; if(f[k][x]+f[mid][x]>f[k][y]+f[mid][y])l=mid+1; else res=mid,r=mid-1; } return res; } int main() { freopen("easyproblem.in","r",stdin); freopen("easyproblem.out","w",stdout); int n,k,i,j,l; n=read();k=read(); if(k>=n)return 0*puts("0"); for(i=1;i<=n;++i)x[i]=read(),w[i]=read(); for(i=1;i<=n;++i)for(j=i;j--;) f[i][j]=f[i][j+1]+1LL*w[j+1]*(x[i]-x[j+1]); for(i=1;i<=n;++i)f[0][i]=INF; if(n<=300) for(l=1;l<=k;++l)for(i=l;i<=n;++i)for(f[l][i]=INF,j=l-1;j<i;++j) f[l][i]=min(f[l][i],f[l-1][j]+f[i][j]); else for(l=1;l<=k;++l)for(ql=0,qr=-1,i=l;i<=n;++i) { while(ql<qr&&(t[qr]=cal(l-1,n,i-1,q[qr]))<=t[qr-1])--qr; if(ql==qr)t[qr]=cal(l-1,n,i-1,q[qr]);q[++qr]=i-1; while(ql<qr&&t[ql]<=i)++ql; f[l][i]=f[l-1][q[ql]]+f[i][q[ql]]; } cout<<f[k][n]; fclose(stdin);fclose(stdout);return 0; }
T3.你的三角形已如风中残烛
题目大意:题名很劲爆,题意是这样的:给定一张n个点的无向图,选出若干个点,使得由这些点组成的三角形个数与选的点个数比值最大,输出方案。(n<=50)
思路:我写的小数据暴搜,大数据自己构造几个比较优而且好找的解取最优输出,得分64。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cstdlib> inline int read() { int x;char c; while((c=getchar())<'0'||c>'9'); for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0'; return x; } #define MN 50 int n,g[MN+5][MN+5],a[MN+5],an,u[MN+5]; int px[MN*MN+5],py[MN*MN+5],cnt; double ans; void dfs(int x,int k,int s) { int i; if(x>n) { if((k?(double)s/k:0)>=ans) { ans=k?(double)s/k:0; for(an=0,i=1;i<=n;++i)if(u[i])a[++an]=i; } return; } u[x]=0;dfs(x+1,k,s); for(i=1;i<=cnt;++i)if(g[x][px[i]]&&g[x][py[i]])++s; for(i=1;i<x;++i)if(u[i]&&g[x][i])px[++cnt]=i,py[cnt]=x; u[x]=1;dfs(x+1,k+1,s); for(i=1;i<x;++i)if(u[i]&&g[x][i])--cnt; } void cal() { int i,j,s=0,k=0; for(cnt=0,i=1;i<=n;++i)if(u[i]) { ++k; for(j=1;j<=cnt;++j)if(g[i][px[j]]&&g[i][py[j]])++s; for(j=1;j<i;++j)if(u[j]&&g[i][j])px[++cnt]=i,py[cnt]=j; } if((k?(double)s/k:0)>ans) { ans=k?(double)s/k:0;an=0; for(i=1;i<=n;++i)if(u[i])a[++an]=i; } } void sb() { int i,j,k,l; for(i=1;i<=n;++i)u[i]=1;cal(); for(i=1;i<=n;++i)u[i]=0,cal(),u[i]=1; for(i=1;i<=1000;++i,cal())for(j=1;j<=n;++j)u[j]=rand()&1; for(i=1;i<=n;++i)for(j=i+1;j<=n;++j)for(k=j+1;k<=n;++k) if(g[i][j]&&g[j][k]&&g[k][i]){for(l=1;l<=n;++l)u[l]=0;u[i]=u[j]=u[k]=1;cal();} for(i=1;i<=n;++i)for(j=i+1;j<=n;++j)for(k=j+1;k<=n;++k)for(l=k+1;l<=n;++l) if(g[i][j]&&g[i][k]&&g[i][l]&&g[j][k]&&g[j][l]&&g[k][l]) {for(int p=1;p<=n;++p)u[p]=0;u[i]=u[j]=u[k]=u[l]=1;cal();} } int main() { srand(23333); freopen("triangle.in","r",stdin); freopen("triangle.out","w",stdout); int i,j; n=read(); for(i=1;i<=n;++i)for(j=1;j<=n;++j)g[i][j]=read(); if(n<=20)dfs(1,0,0); else sb(); printf("%d\n",an); for(i=1;i<=an;++i)printf("%d ",a[i]); fclose(stdin);fclose(stdout);return 0; }
正解:二分答案后最大流,每个三角形建一个点,向T连1,组成该三角形的三个点向它连INF,每次S向点连二分出的答案,则若三角形连向T的边均满流则当前二分答案偏大,否则偏小。二分出答案后删掉所有从S开始只走还有流量的边能到的点,剩下的点即为方案。复杂度O(MaxFlow(n^3,n^3))。
#include<cstdio> #include<cmath> #include<cstring> inline int read() { int x;char c; while((c=getchar())<'0'||c>'9'); for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0'; return x; } #define MN 50 #define MV 19650 #define ME 78450 #define S MV+1 #define T MV+2 #define eps 1e-6 #define INF 1e6 struct edge{int nx,t;double w;}e[ME*2+5]; int g[MN+5][MN+5],h[MV+5],en=1,q[MV+5],qn,d[MV+5],c[MV+5]; inline void ins(int x,int y,double w) { e[++en]=(edge){h[x],y,w};h[x]=en; e[++en]=(edge){h[y],x,0};h[y]=en; } bool bfs() { int i,j; memset(d,0,sizeof(d)); for(d[q[i=qn=0]=S]=1;i<=qn;++i)for(j=c[q[i]]=h[q[i]];j;j=e[j].nx) if(e[j].w>eps&&!d[e[j].t])d[q[++qn]=e[j].t]=d[q[i]]+1; return d[T]; } double dfs(int x,double r) { if(x==T)return r; double k,u=0; for(int&i=c[x];i;i=e[i].nx)if(e[i].w>eps&&d[e[i].t]==d[x]+1) { k=dfs(e[i].t,r-u<e[i].w?r-u:e[i].w); u+=k;e[i].w-=k;e[i^1].w+=k; if(fabs(u-r)<eps)return u; } return d[x]=0,u; } void dfs(int x) { d[x]=1; for(int i=h[x];i;i=e[i].nx)if(!d[e[i].t]&&e[i].w>eps)dfs(e[i].t); } int main() { int n=read(),i,j,k,cnt=n;double l,r,mid,ans; for(i=1;i<=n;++i)ins(S,i,0); for(i=1;i<=n;++i)for(j=1;j<=n;++j)g[i][j]=read(); for(i=1;i<=n;++i)for(j=1;j<i;++j)for(k=1;k<j;++k) if(g[i][j]&&g[j][k]&&g[k][i])ins(++cnt,T,1),ins(i,cnt,1),ins(j,cnt,1),ins(k,cnt,1); for(l=0,r=400,k=0;k<=50;++k) { mid=(l+r)/2; for(i=h[S];i;i=e[i].nx)e[i].w=mid,e[i^1].w=0; memset(d,0,sizeof(d)); for(d[q[i=qn=0]=S]=1;i<=qn;++i)for(j=h[q[i]];j;j=e[j].nx) { if(!d[e[j].t])d[q[++qn]=e[j].t]=d[q[i]]+1; if(d[e[j].t]>d[q[i]])e[j].w+=e[j^1].w,e[j^1].w=0; } for(ans=0;bfs();)ans+=dfs(S,INF); for(i=h[T];i;i=e[i].nx)if(e[i].w>eps&&e[i].w<1-eps)break; (i?l:r)=mid; } memset(d,cnt=0,sizeof(d));dfs(S); for(i=1;i<=n;++i)if(!d[i])++cnt; printf("%d\n",cnt); for(i=1;i<=n;++i)if(!d[i])printf("%d ",i); }