您的位置:首页 > 其它

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

题目

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: