2017-11-5离线赛总结
2017-11-05 20:17
281 查看
题目
失分小结
估分
实际分数
题解
T1
P100
CODE
T2
P70-80
CODE
P100
CODE
T3
P10
P30
P50
CODE
P60
CODE
P100
CODE
(第三题切分切太多dfs没写…)
第二题炸了其实最好能写到80
//加读入挂16ms,不加30ms
[b]CODE[/b]
普通MST可以写到P40,但是由于平面图的特殊性,可以进行各种优化.
定义f[i][j][0 or 1]表示左边第i个点与右边第j个点一下的点已经处理完毕,左i点与右j点连不连通.那么f[i][j]可以从f[i−1][j]或f[i][j−1]转移.
转移方程见CODE.
维护i,j相邻,这样最多会用到n+m个状态.
显然i,j都可以滚动.
[b]CODE[/b]
[b]CODE[/b]
这里加上了P50的代码
[b]CODE[/b]
而由于可以通过可行性剪枝剪掉很多不合法的情况,dfs还是很快滴。位置的状态可以用二进制压位表示,极限时间复杂度为O(n×nm).
另外,由于m是指数,因此当m过大而n不大时可以采用P50的状压dp.
[b]CODE[/b]
失分小结
估分
实际分数
题解
T1
P100
CODE
T2
P70-80
CODE
P100
CODE
T3
P10
P30
P50
CODE
P60
CODE
P100
CODE
题目
3808,3809,3810失分小结
估分
100+40+10(第三题切分切太多dfs没写…)
实际分数
100+20+30第二题炸了其实最好能写到80
题解
T1
P100
我是直接敲P100代码实话说我还真不知道P70怎么写…//加读入挂16ms,不加30ms
[b]CODE[/b]
#include<cstdio> #define N 200005 typedef long long LL; const LL P=1e9+7; int A ;//数组还可以优化 inline void rd(int &x) { x=0;char c; while(c=getchar(),c<48); do x=(x<<3)+(x<<1)+(c&15); while(c=getchar(),c>47); } int main() { int n; rd(n); for(int i=0; i<=n; i++)rd(A[i]); LL sum=(A[0]+A[1])%P; LL res=(2LL*A[0]*A[1])%P; LL POW=1; for(int i=2; i<=n; i++) { POW=(POW<<1)%P; res=(((res+sum*A[i])%P)<<1)%P; sum=(sum+A[i]*POW)%P; } printf("%lld\n",res); return 0; }
T2
平面图上的MST.普通MST可以写到P40,但是由于平面图的特殊性,可以进行各种优化.
P70-80
[b]CODE[/b]#include<cstdio> #include<cmath> #include<algorithm> #define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;i++) #define M 1000005 using namespace std; typedef long long LL; int Y1[M],Y2[M],fa[M*2]; LL Sum1[M],Sum2[M]; struct node { int x,y; double len; } E[5*M]; template<class T>void rd(T &x) { x=0;static char p; while(p=getchar(),p<48); do x=(x<<1)+(x<<3)+(p&15); while(p=getchar(),p>47); } bool cmp(node w1,node w2) {return w1.len<w2.len;} double ans=0; LL sqr(LL x) {return x*x;} int getfa(int x) {return fa[x]==x?x:fa[x]=getfa(fa[x]);} int main() { int n,m,x1,x2,tot=0; rd(n),rd(m),rd(x1),rd(x2); FOR(i,1,n)rd(Y1[i]),Sum1[i]=Sum1[i-1]+Y1[i]; FOR(i,1,m)rd(Y2[i]),Sum2[i]=Sum2[i-1]+Y2[i]; int tmp=sqr(x1-x2); FOR(i,1,n-1)E[++tot]=(node) {i,i+1,Y1[i+1]}; FOR(i,1,m-1)E[++tot]=(node) {n+i,n+i+1,Y2[i+1]}; 198f7 FOR(i,1,n) { int id=lower_bound(Sum2+1,Sum2+1+m,Sum1[i])-Sum2; if(id==m+1)id--; if(Sum2[id]==Sum1[i])E[++tot]=(node) {i,n+id,x2-x1}; else if(Sum2[id]<Sum1[i])E[++tot]=(node) { i,n+id,sqrt(tmp+sqr(Sum1[i]-Sum2[id]))}; else { E[++tot]=(node) {i,n+id,sqrt(tmp+sqr(Sum1[i]-Sum2[id]))}; id--; if(!id)continue; E[++tot]=(node) {i,n+id,sqrt(tmp+sqr(Sum1[i]-Sum2[id]))}; } } sort(E+1,E+1+tot,cmp); FOR(i,1,n+m)fa[i]=i; FOR(i,1,tot) { int x=getfa(E[i].x),y=getfa(E[i].y); if(x==y)continue; ans+=E[i].len; fa[x]=y; } printf("%.2f\n",ans); return 0; }
P100
显然最简(bian)单(tai)的还是dp. 时间复杂度O(n+m).定义f[i][j][0 or 1]表示左边第i个点与右边第j个点一下的点已经处理完毕,左i点与右j点连不连通.那么f[i][j]可以从f[i−1][j]或f[i][j−1]转移.
转移方程见CODE.
维护i,j相邻,这样最多会用到n+m个状态.
显然i,j都可以滚动.
[b]CODE[/b]
#include<cstdio> #include<cmath> #include<algorithm> #define M 1000005 using namespace std; template<typename T>void rd(T &x) { x=0;static char c; while(c=getchar(),c<48); do x=(x<<3)+(x<<1)+(c&15); while(c=getchar(),c>47); } int Y1[M],Y2[M]; double f[M][2][2]; int n,m; long long x1,x2,x; double t; inline long long sqr(register int x){return 1LL*x*x;} inline double dist(int i,int j) {return sqrt(x+sqr(Y1[i]-Y2[j]));} int main() { rd(n),rd(m),rd(x1),rd(x2); x=sqr(x1-x2); for(int i=1; i<=n; i++) { rd(Y1[i]); if(i>1)Y1[i]=Y1[i]+Y1[i-1]; } for(int i=1; i<=m; i++) { rd(Y2[i]); if(i>1)Y2[i]=Y2[i]+Y2[i-1]; } f[1][1][1]=dist(1,1); int j=1,cur=1; for(int i=1; i<=n; i++) { if(i>1) { t=dist(i,j); f[i][cur][1]=min(f[i-1][cur][1]+Y1[i]-Y1[i-1],min(f[i-1][cur][1]+t,f[i-1][cur][0]+Y1[i]-Y1[i-1]+t)); f[i][cur][0]=min(f[i-1][cur][1],f[i-1][cur][0]+Y1[i]-Y1[i-1]); } while(j<m&&(Y1[i]>=Y2[j]||i==n)) { t=dist(i,++j); cur=!cur; f[i][cur][1]=min(f[i ][!cur][1]+Y2[j]-Y2[j-1],min(f[i][!cur][1]+t,f[i][!cur][0]+Y2[j]-Y2[j-1]+t)); f[i][cur][0]=min(f[i ][!cur][1],f[i][!cur][0]+Y2[j]-Y2[j-1]); } } printf("%.2lf\n",f [m&1][1]); return 0; }
T3
P10
n<=7:O(nn)枚举每个人的编号模拟.(dfs)P30
n<=10:O(n!)枚举排列.P50
n<=18:状压dp,dp[i][j]表示第i个人固定了位置后每个位置占用状态为j.[b]CODE[/b]
#include<cstdio> #include<memory.h> #include<cmath> #define S 20 #define FOR(i,x,y) for(int i=(x);i<=(y);i++) using namespace std; template<typename T>inline void Rd(T &x) { x=0; register char c; while(c=getchar(),c<48); do x=(x<<3)+(x<<1)+(c&15); while(c=getchar(),c>47); } int n,m,P; int B[35]; bool mark[S]; int dp[S][1<<S]; int main() { scanf("%d%d%d",&n,&m,&P); FOR(i,1,m) { int x,pos; scanf("%d%d",&x,&pos); mark[x]=1; B[x]=pos; B[x]--; } int tot=(1<<n)-1; memset(dp,-1,sizeof dp); dp[0][0]=1; FOR(k,1,n)FOR(i,0,tot)if(~dp[k-1][i]) FOR(j,B[k],n-1)if(!(i&(1<<j))) { if(!~dp[k][i|(1<<j)]) dp[k][i|(1<<j)]=0; dp[k][i|(1<<j)]+=dp[k-1][i]; dp[k][i|(1<<j)]%=P; if(mark[k])break; } printf("%d\n",dp [tot]==-1?0:dp [tot]); return 0; }
P60
m==0和m==1时特判。这样多P10.这里加上了P50的代码
[b]CODE[/b]
#include<cstdio> #include<cstdlib> #include<memory.h> #include<algorithm> #define FOR(i,a,b) for(int i=(a),i##_END_=(b);i<=i##_END_;i++) #define N 35 #define S 20 using namespace std; int peo ; struct node { int p,q; bool operator<(const node _)const {return p<_.p;} } A ; template<typename T>inline void Rd(T &x) { x=0;register char c; while(c=getchar(),c<48); do x=(x<<3)+(x<<1)+(c&15); while(c=getchar(),c>47); } int B ; bool mark[S]; int dp[S][1<<S]; int main() { int n,m,M; scanf("%d %d %d",&n,&m,&M); if(m==0) { long long ans=1; for(int i=2; i<=n; i++) ans=(ans*i)%M; printf("%lld\n",ans); exit(0); } if(m==1) { long long f ,A ,ans=0; int p,q; scanf("%d %d",&p,&q); A[0]=f[0]=f[1]=1,A[1]=p-1; for(int i=2; i<=n; i++) { f[i]=(f[i-1]*i)%M; A[i]=(A[i-1]*(p-i))%M; } for(int i=0; i<=min(p-1,n-q); i++) { ans=(ans+A[i]*f[n-1-i])%M; } printf("%lld\n",ans); exit(0); } FOR(i,1,m) { int x,pos; scanf("%d%d",&x,&pos); mark[x]=1; B[x]=pos; B[x]--; } int tot=(1<<n)-1; memset(dp,-1,sizeof dp); dp[0][0]=1; FOR(k,1,n)FOR(i,0,tot)if(~dp[k-1][i]) FOR(j,B[k],n-1)if(!(i&(1<<j))) { if(!~dp[k][i|(1<<j)]) dp[k][i|(1<<j)]=0; dp[k][i|(1<<j)]+=dp[k-1][i]; dp[k][i|(1<<j)]%=M; if(mark[k])break; } printf("%d\n",dp [tot]==-1?0:dp [tot]); return 0; }
P100
首先有一个比较显然的性质:若第i个人本来要到j位置 现在只能到k位置 那么在j到k之间的人一定编号比i小,可以dfs每个固定位置的人最后在什么位置,那么每次需要知道当前已经有人的位置和已经选好位置的人数。在比当前人编号小的人里面可以选出一些在当前人的前面,这些人的位置又不固定,所以要用排列组合算出方案数。全部确定完之后可能还有人没有固定要再排列一下。而由于可以通过可行性剪枝剪掉很多不合法的情况,dfs还是很快滴。位置的状态可以用二进制压位表示,极限时间复杂度为O(n×nm).
另外,由于m是指数,因此当m过大而n不大时可以采用P50的状压dp.
[b]CODE[/b]
#include<cstdio> #include<memory.h> #include<algorithm> #define N 35 #define FOR(i,a,b) for(int i=(a);i<=(b);i++) int n,m,M; struct node {int p,q;} D[10]; int C ,fac ,A ; bool mark ; int ans; bool cmp(node _,node __){return _.p<__.p;} int x=1; void dfs(int res,int tmp) { res+=D[x].p-D[x-1].p-1; if(x>m) { tmp=1ll*tmp*fac[res]%M; ans=(ans+tmp)%M; return; } int sum=0; bool used ; memcpy(used,mark,sizeof used); for(int i=D[x].q; i<=n; i++)if(!mark[i]) { if(sum>res)break; mark[i]=1; x++,dfs(res-sum,1ll*tmp*A[res][sum]%M),x--; sum++; } memcpy(mark,used,sizeof mark); } int main() { scanf("%d%d%d",&n,&m,&M); FOR(i,1,m)scanf("%d%d",&D[i].p,&D[i].q); C[0][0]=C[1][0]=C[1][1]=fac[0]=fac[1]=1; FOR(i,2,30) { C[i][0]=C[i][i]=1; fac[i]=1ll*fac[i-1]*i%M; FOR(j,1,i-1)C[i][j]=(C[i-1][j]+C[i-1][j-1])%M; } FOR(i,0,30)FOR(j,0,i)A[i][j]=1LL*C[i][j]*fac[j]%M; std::sort(D+1,D+1+m,cmp); D[m+1].p=n+1; D[0].p=0; dfs(0,1); printf("%d\n",ans); return 0; }
相关文章推荐
- 2017-11-5离线赛总结(NOIP七连测第三场)
- 2017.10.30离线赛总结
- 离线赛20171010总结
- 完整的ubuntu镜像源/本地源/更新源/离线升级包!制作总结!
- 2017.10.15离线赛总结
- 离线缓存与客户端存储总结
- 2017-10-29离线赛总结
- 2017-11-3离线赛总结
- 2017-10-6离线赛总结
- 2017.10.10离线赛总结
- 2017.10.27离线赛总结
- 2017.10.29离线赛总结
- 10.10离线赛总结
- 2017.10.17离线赛总结
- Google Docs离线功能的简单总结
- 2017-11-6离线赛总结
- 2017-11-8离线赛总结
- PAIP.paip.手机离线ROOT过程总结
- 离线工程代码学习总结
- CDH5 Hadoop集群完全离线安装步骤总结