- 上午
- 模拟考试
- Prob.1(AC)位运算的拆位操作。
- Prob.2(AC)模型转化,求不成环的边集最大有多少条边,并查集。
- Prob.3(AC)用到一个小性质 a|b=a+b-a&b
- 感觉今天题算简单。
- 下午
- BOZJ 1067 [SCOI2007]降雨量1mol的特判和分类讨论,改了好久。
代码:
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 50005 #define INF 0x3f3f3f3f using namespace std; struct info{ int year,amount; }p[MAXN]; struct SGT{ #define ls lson[u] #define rs rson[u] int rt,sz,len; int init[MAXN<<1],lson[MAXN<<1],rson[MAXN<<1],maxi[MAXN<<1]; void pushup(int u){ maxi[u]=max(maxi[ls],maxi[rs]); } void build(int &u,int l,int r){ u=++sz; if(l==r){ maxi[u]=init[l]; return; } int mid=(l+r)>>1; build(ls,l,mid); build(rs,mid+1,r); pushup(u); } int query(int u,int l,int r,int al,int ar){ if(al<=l&&r<=ar) return maxi[u]; int mid=(l+r)>>1,nmaxi=-INF; if(al<=mid) nmaxi=max(nmaxi,query(ls,l,mid,al,ar)); if(mid<ar) nmaxi=max(nmaxi,query(rs,mid+1,r,al,ar)); return nmaxi; } #undef ls #undef rs }t; int n,m; int binary(int x){ int l=1,r=n,mid,ans=n+1; while(l<=r){ mid=(l+r)>>1; p[mid].year>=x? ans=mid,r=mid-1:l=mid+1; } return ans; } int main(){ freopen("1067.in","r",stdin); freopen("1067.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&p[i].year,&p[i].amount),t.init[i]=p[i].amount; t.len=n; t.build(t.rt,1,t.len); scanf("%d",&m); for(int i=1,a,b,l,r;i<=m;i++){ scanf("%d%d",&a,&b); if(a>b) {printf("false"); continue;} l=binary(a); r=binary(b); if(p[l].year!=a&&p[r].year!=b) printf("maybe"); if(p[l].year==a&&p[r].year!=b){ if(l+1<r&&t.query(t.rt,1,t.len,l+1,r-1)>=p[l].amount) printf("false"); else printf("maybe"); } if(p[l].year!=a&&p[r].year==b){ if(l<r&&t.query(t.rt,1,t.len,l,r-1)>=p[r].amount) printf("false"); else printf("maybe"); } if(p[l].year==a&&p[r].year==b){ if(p[r].amount>p[l].amount) printf("false"); else { if(l+1<r&&t.query(t.rt,1,t.len,l+1,r-1)>=p[r].amount) printf("false"); else{ if(r-l+1==b-a+1) printf("true"); else printf("maybe"); } } } printf(" "); } return 0; }
- BOZJ 1068 [SCOI2007]压缩
区间dp。
dp[l][r][0/1]表示在l前面有一个M,在区间l-r里面有(无)M的最小长度
(感觉这个状态不好定,是我太弱了吧)
然后转移分3种:
1).dp[l][r][0]=min{dp[l][r][0],dp[l][i][0]+r-i}
表示i~r不压缩
2).如果区间长度(r-l+1)%2==0且前后两段字符相同(s[l~mid]==s[mid+1~r])
dp[l][r][0]=min(dp[l][r][0],dp[l][mid][0]+1)
表示放一个R在mid和mid+1之间
3).dp[l][r][1]=min{dp[l][r][1],min(dp[l][i][0],dp[l][i][1])+1+min(dp[i+1][r][0],dp[i+1][r][1])}
表示在i,i+1之间放一个M,那么l~i和i+1~r就是两个独立的区间了。代码:
#include<cstdio> #include<cstring> #include<iostream> using namespace std; char s[55]; bool vis[55][55]; int dp[55][55][2],n; bool check(int l,int m){ for(int i=0;l+i<m;i++) if(s[l+i]!=s[m+i]) return 0; return 1; } void dfs(int l,int r){ if(vis[l][r]) return; vis[l][r]=1; for(int i=l;i<r;i++) dfs(l,i),dfs(i+1,r); int &ret0=dp[l][r][0],&ret1=dp[l][r][1]; ret0=ret1=r-l+1; //1 for(int i=l;i<r;i++) ret0=min(ret0,dp[l][i][0]+r-i); //2 if((r-l+1)%2==0){ int mid=(l+r)>>1; if(check(l,mid+1)) ret0=min(ret0,dp[l][mid][0]+1); } //3 for(int i=l;i<r;i++) ret1=min(ret1,min(dp[l][i][0],dp[l][i][1])+1+min(dp[i+1][r][0],dp[i+1][r][1])); } int main(){ scanf("%s",s+1); n=strlen(s+1); dfs(1,n); printf("%d",min(dp[1][n][0],dp[1][n][1])); return 0; }
- BOZJ 1070 [SCOI2007]修车
神奇费用流。
把m个员工拆点,分别拆为n个点,
第i个员工的第j个点用编号idx(i,j)表示,这个点代表i号员工修他的倒数第j辆车。
如何建边呢?
1).考虑如果i号员工修的倒数第j辆车(这辆车编号为w)的时间为x,那么这辆车的修理对总时间的贡献为j*x,所以把x号车向idx(i,j)建一条容量为1,费用为j*x的边。
2).S向每辆车建一条容量为1,费用为0的边。
3).m个员工拆成的n*m个点向T建一条容量为1,费用为0的边 。
然后就是最小费用流问题了。代码:
#include<queue> #include<cstdio> #include<cstring> #include<iostream> #define INF 0x3f3f3f3f using namespace std; struct edge{ int to,capo,cost,next; }e[80000]; int head[650]; int n,m,ent=2,S,T; void add(int u,int v,int capo,int cost){ e[ent]=(edge){v,capo,cost,head[u]}; head[u]=ent++; e[ent]=(edge){u,0,-cost,head[v]}; head[v]=ent++; } int idx(int i,int j){ //第i个修理工修倒数第j辆 return n+(j-1)*m+i; } void build(){ for(int i=1,x;i<=n;i++) for(int j=1;j<=m;j++){ scanf("%d",&x); for(int k=1;k<=n;k++) add(i,idx(j,k),1,k*x); } for(int i=1;i<=n;i++) add(S,i,1,0); for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) add(idx(i,j),T,1,0); } bool spfa(int &cost){ static int dis[650],p[650],low[650]; static bool inq[650]; queue<int> q; memset(dis,0x3f,sizeof(dis)); dis[S]=0; q.push(S); low[S]=INF; while(!q.empty()){ int u=q.front(); q.pop(); inq[u]=0; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(!e[i].capo||dis[v]<=dis[u]+e[i].cost) continue; dis[v]=dis[u]+e[i].cost; p[v]=i; low[v]=min(low[u],e[i].capo); if(!inq[v]) inq[v]=1,q.push(v); } } if(dis[T]==INF) return 0; cost+=low[T]*dis[T]; int u=T; while(u!=S){ e[p[u]].capo-=low[T]; e[p[u]^1].capo+=low[T]; u=e[p[u]^1].to; } return 1; } void mincost_maxflow(){ int cost=0; while(spfa(cost)); printf("%.2lf",1.0*cost/n); } int main(){ scanf("%d%d",&m,&n); S=n+n*m+1; T=n+n*m+2; build(); mincost_maxflow(); return 0; }
- BOZJ 1067 [SCOI2007]降雨量1mol的特判和分类讨论,改了好久。
- 晚上
- BOZJ 1071 [SCOI2007]组队
考虑把题目给的式子拆开,并移项:
A*H + B*V <= A*minH + B*minV + C
上式表明,对于已知的 minH 和 minV,如果第i如果满足上式,则他可以加入队伍。
把每个人看成一个三元组(H,V,W=A*H + B*V)
开三个机构体数组,每个数组存下所有人的信息。
第一个数组(a)用于枚举最小高度
第二个数组(b)按 V 排序后,用于枚举最小速度
第三个数组(c)按 W 排序后,用于枚举每个人是否可以加入队伍具体的做法:
枚举第一个数组,固定一个最小高度minH
枚举第二个数组,固定一个最小的速度minV (此时得到不等式右边 Val=A*minH+B*minV+C)
然后枚举第三个数据,看对于当前的 minH 和 minV,可以有多少人加入队伍。
(满足加入队伍的条件是 c[i].w<=Val&&c[i].H>=minH&&c[i].V>=minV)
显然 N^3,会超时尝试优化,利用我们的排序。
如果对于当前的 minH,枚举到 minV(得到 Val(即不等式右边)),
我们发现c数组的前k个元素的w值小于等于 Val,
那么对于下一个枚举的minV'(排序后minV'>minV),得到的Val'必然大于 Val,
所以c数组的前k个元素同样也小于Val',
这意味着我们不需要重新从头开始枚举c数组,
只需要接着上次的k位置一直枚举到这次的前k'个元素(c数组的前k'个元素的w值小于等于 Val')
所以对于每个枚举到的minH,b数组遍历了一次,c数组也只遍历了一次。
即复杂度降到 N^2
另外,还有一个问题,就是前k个元素中选出的满足(minH,minV)这个二元组的人不一定都满足(minH,minV')
因为之前的满足条件的人在V值上只需保证大于minV,但现在需要保证大于minV',
怎么处理呢。发现题目中V的范围很小,所以开一个桶,cc[i]表示满足条件的且v值等于i的人数,
对于一个新的minV'只需把之前的满足条件的人数-cc[minV](minV为上一次枚举到的minV值),
即减掉前k个元素中在上一个minV满足条件,但在当前的minV'下却不满足条件的人。
然后就差不多了。还有几个细节:(坑了我好久)
1).枚举的minH和minV可能相矛盾,需要跳过。
即我们认定minH为最小H,但minV那个人的H更小。
或我们认定minV为最小V,但minH那个人的V更小
2).枚举到的minH和minV不满足不等式,需要跳过。
即对于当前的minH和minV,我们得出一个Val,
但是minH那个人的w或者minV那个人的w大于Val。
3).可能会有相同的V值,要及时清空cc数组
因为存在-cc[minV(上一次枚举的最小速度)]这一操作,但如果有相同的V(即minV==minV')的人的话,
就会重复减去 cc[minV]这个值。
(一些细节搞了我2个多小时,真弱啊,)代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; struct node{ ll h,v,w; }a[5005],b[5005],c[5005]; ll cc[10005]; ll n,p,A,B,C,h,v,mh,mv,val,now,ans; bool cmp1(node x,node y){ return x.v<y.v; } bool cmp2(node x,node y){ return x.w<y.w; } int main(){ scanf("%lld%lld%lld%lld",&n,&A,&B,&C); for(int i=1;i<=n;i++){ scanf("%lld%lld",&h,&v); a[i]=(node){h,v,A*h+B*v}; b[i]=c[i]=a[i]; } sort(b+1,b+n+1,cmp1); sort(c+1,c+n+1,cmp2); for(int i=1;i<=n;i++){ memset(cc,0,sizeof(cc)); p=1; mh=a[i].h; now=0; for(int j=1;j<=n;j++){ now-=cc[b[j-1].v]; cc[b[j-1].v]=0; if(a[i].v<b[j].v||b[j].h<a[i].h) continue; mv=b[j].v; val=A*mh+B*mv+C; if(a[i].w>val||b[j].w>val) continue; while(p<=n&&c[p].w<=val){ if(c[p].h>=mh&&c[p].v>=mv) now++,cc[c[p].v]++; p++; } ans=max(ans,now); } } printf("%lld",ans); return 0; }
- BOZJ 1071 [SCOI2007]组队