• 17.10.24


      • 上午
        • 模拟考试
        • 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 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;
          }
          

  • 相关阅读:
    项目功能规格说明书
    团队工作准则&贡献分配规则
    Scrum Meeting Alpha
    用户需求与NABCD分析
    团队项目选题-博客园移动客户端
    团队作业Week3
    爱情小故事
    高手遇事的处理方法,学会你也是高手
    富人思维--目标导向
    有一种失败叫瞎忙
  • 原文地址:https://www.cnblogs.com/zj75211/p/7726952.html
Copyright © 2020-2023  润新知