• 17.10.18


      • 又来水博客了……
      • 上午
        • BZOJ 1040 [ZJOI2008]骑士

    由于每颗树上只有一个环,
    所以dfs找出环,取出上面相邻的两个点(x,y)
    把x,y之间的双向边删除,分别对x,y跑一个dp(因为现在已成树)
    然后取出x为起点且不取x的最大值以及 y为起点且不取y的最大值,把两者中的最大值加入ans
       
    注意在dfs时要把一个连通块跑完(全部打上vis标记),不要找到环之后就马上退出。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define MAXN 1000005
    #define ll long long
    using namespace std;
    struct edge{
    	int to,next;
    }e[MAXN*2];
    bool del[MAXN*2];
    int head[MAXN],val[MAXN],vis[MAXN];
    ll f[MAXN][2],ans,now;
    int n,ent=2;
    void add(int u,int v){
    	e[ent]=(edge){v,head[u]};
    	head[u]=ent++;
    }
    void dp(int u,int fa){
    	for(int i=head[u];i;i=e[i].next)if(!del[i]){
    		int v=e[i].to;
    		if(v==fa) continue;
    		dp(v,u);
    		f[u][0]+=max(f[v][1],f[v][0]);
    		f[u][1]+=f[v][0];
    	}
    	f[u][1]+=val[u];
    }
    void enter(int u){
    	memset(f,0,sizeof(f));
    	dp(u,0);
    	now=max(now,f[u][0]);
    }
    void dfs(int u,int from){
    	vis[u]=1;
    	for(int i=head[u];i;i=e[i].next)if((i^from)!=1){
    		int v=e[i].to;
    		if(vis[v]==1){
    			now=0; del[i]=del[i^1]=1;
    			enter(u);
    			enter(v);
    			ans+=now;
    		}
    		else if(!vis[v]) dfs(v,i);
    	}
    	vis[u]=2;
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1,j;i<=n;i++){
    		scanf("%d%d",&val[i],&j);
    		add(i,j); add(j,i);	
    	}
    	for(int i=1;i<=n;i++)
    		if(!vis[i]) dfs(i,0);
    	printf("%lld",ans);
    	return 0;
    }
    
        • Ztraveler继续选讲,很绝望
      • 下午
        • BZOJ 1041 [HAOI2008]圆上的整点

    看大佬题解分析:http://hzwer.com/1457.html
    另外在补充一个当时自己的疑点:
    如果找到一组合法的(A,B),那是否只对应唯一一组(x,y)(x>0,y>0)
       
    由 A=(R-x)/d B=(R+x)/d
    假设存在x=x1和x=x2(x1!=x2) 都满足上式的那一组(A,B)(即假设该(A,B)对应两组解)
    令    C1=R-x1 D1=R+x1 d1=gcd(C1,D1)
          C2=R-x2 D2=R+x2 d2=gcd(C2,D2)
    显然 C1+D1=C2+D2=2R ★
    又∵C1/d1=A C2/d2=A 即 C1/d1=C2/d2 ①
       D1/d1=B D2/d2=B 即 D1/d1=D2/d2 ②
    ∴①+ ②:(C1+D1)/d1=(C2+D2)/d2
    由于 ★
    ∴上式 : 2R/d1=2R/d2
    ∴d1=d2 ③
    将 ③带入 ①②得 C1=C2 D1=D2
    ∴x1=x2,两个解相同。
    即不存在两个不同的解满足找到一组合法的 (A,B)
    即一组(A,B)只唯一对应一组 (x,y)
    所以找到一组合法的 (A,B) 后 ans+=1

    代码:

    #include<cstdio>
    #include<cmath>
    #include<iostream>
    #define ll long long
    using namespace std;
    ll r,ans;
    int gcd(ll a,ll b){
    	while(a^=b^=a^=b%=a);
    	return b;
    }
    bool check(ll a, double B){
    	if(B!=floor(B)) return 0;
    	ll b=floor(B);
    	return a!=b&&gcd(a*a,b*b)==1;
    }
    int main(){
    	scanf("%lld",&r);
    	for(int d=1;d<=sqrt(2*r);d++){
    		if((2*r)%d) continue;
    		for(int a=1;a<=sqrt(r/d);a++){
    			double b=sqrt(2*r/d-a*a);
    			if(check(a,b)) ans++;
    		}
    		if(2*r/d==d)continue;
    		for(int a=1;a<=sqrt(d/2);a++){
    			double b=sqrt(d-a*a);
    			if(check(a,b)) ans++;
    		}
    	}
    	printf("%lld",ans*4+4);
    	return 0;
    }
    
        • BZOJ 1044 [HAOI2008]木棍分割

    先二分出答案,
    然后跑dp
    令 dp[j][i]表示枚举到了第i根,并且分成了j段的方案数
    易得 dp[j][i]= ∑dp[j-1][k] (且 第k+1段到第i段的长度和小于l)
    显然 可取的dp值为连续的一段,用队列维护(滑动窗口吧)那一段的dp值,然后直接转移,就不用枚举k了
    另外还要滚动数组

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define MAXN 50005
    using namespace std;
    const int mod=10007;
    int len[MAXN],dp[2][MAXN];
    int n,m,anslen,ansnum; 
    void Readin(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++) scanf("%d",&len[i]);
    }
    bool Check(int limit){
    	int k=0,sum=0;
    	for(int i=1;i<=n+1;i++){
    		if(i==n+1||sum+len[i]>limit) k++,sum=0;
    		sum+=len[i];
    	}
    	return k<=m+1;
    }
    void Binary(){
    	int l=0,r=0,mid;
    	for(int i=1;i<=n;i++) l=max(l,len[i]),r+=len[i];
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(Check(mid)) anslen=mid,r=mid-1;
    		else l=mid+1;
    	}
    	printf("%d ",anslen);
    }
    void DP(){
    	int sumlen,sumdp,l,ans=0,*x=dp[0],*y=dp[1];
    	x[0]=1;
    	for(int j=1;j<=m+1;j++){
    		swap(x,y); x[0]=0;
    		l=0;sumlen=0; sumdp=y[0];
    		for(int i=1;i<=n;i++){
    			sumlen+=len[i];
    			while(sumlen-len[l]>anslen){
    				sumlen-=len[l];
    				sumdp=(sumdp-y[l]+mod)%mod;
    				l++;
    			}
    			x[i]=sumdp;
    			sumdp=(sumdp+y[i])%mod;
    		}
    		ans=(ans+x[n])%mod;
    	}
    	printf("%d",ans);
    }
    int main(){
    	Readin();
    	Binary();
    	DP(); 
    	return 0;
    }
    
        • BZOJ 1045 [HAOI2008] 糖果传递

    蓝皮书原题,
    数据小时可以用费用流做(负载平衡问题)(好像更麻烦诶)
    数据大时就只能按书上的方法做了。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    #define MAXN 1000005
    using namespace std;
    ll A[MAXN],C[MAXN],M,mid,ans;
    int n;
    ll Abs(ll x){
    	return x>0?x:-x;
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++) scanf("%lld",&A[i]),M+=A[i];
    	M/=n; C[1]=0;
    	for(int i=2;i<=n;i++)
    		C[i]=C[i-1]+A[i-1]-M;
    	sort(C+1,C+n+1);
    	mid=C[n/2+1];
    	for(int i=1;i<=n;i++) ans+=Abs(mid-C[i]);
    	printf("%lld",ans);
    	return 0;
    }
        • BZOJ 1046 [HAOI2007]上升序列

    先用nlogn的算法求出f[i]表示以i位置开始的最长上升子序列的长度
    (反着求最长下降子序列)
    然后就贪心了:
    对于输入的len,从前往后枚举每个位置,
    如果这个位置的f值大于等于len,那么该位置必将是在答案中
    然后记下这个位置的值,同时len--,继续上面的操作直到len=0;

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define MAXN 10005 
    using namespace std;
    int A[MAXN],f[MAXN],nxt[MAXN];
    int tmp[MAXN],ANS[MAXN];
    int n,m,len,cnt;
    int binary(int *C,int r,int val){
    	int l=1,mid,ans;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(C[mid]<=val) ans=mid,r=mid-1;
    		else l=mid+1;
    	}
    	return ans;
    }
    void LIS(){
    	cnt=0;
    	for(int i=n;i>=1;i--){
    		if(i==n||A[i]<tmp[cnt]){
    			tmp[++cnt]=A[i];
    			f[i]=cnt; 
    		}
    		else{
    			int l=binary(tmp,cnt,A[i]);
    			tmp[l]=A[i];
    			f[i]=l;
    		}
    	}
    }
    void solve(int l){
    	int p=0;
    	for(int i=1;i<=n&&l;i++)
    		if(f[i]>=l&&A[i]>ANS[p]){
    			ANS[++p]=A[i];
    			l--;
    		}
    	for(int i=1;i<p;i++) printf("%d ",ANS[i]);
    		printf("%d",ANS[p]);
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++) scanf("%d",&A[i]);
    	LIS();
    	scanf("%d",&m);
    	while(m--){
    		scanf("%d",&len);
    		if(len<=cnt) solve(len);
    		else printf("Impossible");
    		printf("
    ");
    	}
    	return 0;
    }
    
      • 晚上
        • BZOJ 1047 [HAOI2007]理想的正方形

    单调队列维护
    (本来想写写二维RMQ的,但无奈空间不够、、、)

    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define node(a,b) (node){a,b}
    using namespace std;
    struct node{
    	int val,pos;
    }qmax[1005],qmin[1005];
    int colmax[1005][1005],colmin[1005][1005],mp[1005][1005];
    int A,B,N,lmax,lmin,rmax,rmin,ans=0x3f3f3f3f;
    void read(int &x){
    	static int f; static char ch;
    	x=0; f=1; ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    	while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    	x=x*f;
    }
    int main(){
    	read(A); read(B); read(N);
    	for(int i=1;i<=A;i++)
    		for(int j=1;j<=B;j++)
    			read(mp[i][j]);
    	for(int j=1;j<=B;j++){
    		lmax=lmin=1;rmax=rmin=0;
    		for(int i=1;i<=A;i++){
    			while(lmin<=rmin&&mp[i][j]<qmin[rmin].val) rmin--;
    			qmin[++rmin]=node(mp[i][j],i);
    			while(lmin<=rmin&&qmin[lmin].pos+N-1<i) lmin++;
    			colmin[i][j]=qmin[lmin].val;
    			
    			while(lmax<=rmax&&mp[i][j]>qmax[rmax].val) rmax--;
    			qmax[++rmax]=node(mp[i][j],i);
    			while(lmax<=rmax&&qmax[lmax].pos+N-1<i) lmax++;
    			colmax[i][j]=qmax[lmax].val;
    		}
    	}	
    	for(int i=N;i<=A;i++){
    		lmax=lmin=1;rmax=rmin=0;
    		for(int j=1;j<=B;j++){
    			while(lmin<=rmin&&colmin[i][j]<qmin[rmin].val) rmin--;
    			qmin[++rmin]=node(colmin[i][j],j);
    			while(lmin<=rmin&&qmin[lmin].pos+N-1<j) lmin++;
    			
    			while(lmax<=rmax&&colmax[i][j]>qmax[rmax].val) rmax--;
    			qmax[++rmax]=node(colmax[i][j],j);
    			while(lmax<=rmax&&qmax[lmax].pos+N-1<j) lmax++;
    			
    			if(j>=N) ans=min(ans,qmax[lmax].val-qmin[lmin].val);
    		} 
    	} 
    	printf("%d",ans);
    	return 0;
    }
        • BZOJ 1048 [HAOI2007]分割矩阵

    记忆化搜索
    dp[x1][y1][x2][y2][m] 表示在左上角为(x1,y1)右下角为(x2,y2)的矩形中砍m次的最小值((val-ave)^2)

    代码:

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    int mp[12][12],sum[12][12];
    bool vis[12][12][12][12][12];
    double dp[12][12][12][12][12],ave;
    int a,b,n,tot;
    double dfs(int x1,int y1,int x2,int y2,int m){
    	if(vis[x1][y1][x2][y2][m]) return dp[x1][y1][x2][y2][m];
    	vis[x1][y1][x2][y2][m]=1;
    	double &ret=dp[x1][y1][x2][y2][m];
    	ret=1e9;
    	if(!m){
    		ret=sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1];
    		ret=(1.0*ret-ave)*(1.0*ret-ave);
    		return ret;
    	}
    	for(int k=0;k<m;k++)
    		for(int i=x1;i<x2;i++)
    			ret=min(ret,dfs(x1,y1,i,y2,k)+dfs(i+1,y1,x2,y2,m-1-k));
    	for(int k=0;k<m;k++)
    		for(int j=y1;j<y2;j++)
    			ret=min(ret,dfs(x1,y1,x2,j,k)+dfs(x1,j+1,x2,y2,m-1-k));
    	return ret;
    }
    int main(){
    	scanf("%d%d%d",&a,&b,&n);
    	for(int i=1;i<=a;i++)
    		for(int j=1;j<=b;j++){
    			scanf("%d",&mp[i][j]);
    			tot+=mp[i][j];
    			sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+mp[i][j];
    		}
    	ave=1.0*tot/n;
    	double ans=dfs(1,1,a,b,n-1);
    	printf("%.2lf",sqrt(ans/n));
    	return 0;
    }
    
        • BZOJ 1049 [HAOI2006]数字序列

    对于第一问, 权值减下标后跑一个nlogn 的最长不降子序列
    然后第二问就很绝望了,网上的证明都不太清楚(反正我没看懂,只能感性理解了)。。。
    用g[i]表示1-i的序列修改的最小代价。
    结论是 对于 j,i 如果 f[j]+1=f[i] 且 b[j]<=b[i](b为新数列,即权值减下标后的数列) 的话
    则区间(j,i)的最优修改方法是:存在一个k(j<=k<i)使得b[j]~b[k]等于b[j],b[k+1]~b[i]等于b[i]
    (由于是随机数据,看似最坏情况下n^2的复杂度不会超时)

    代码:

    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define MAXN 35005
    #define ll long long
    #define INF (1ll<<50)
    using namespace std;
    ll a[MAXN],b[MAXN],prefix[MAXN],suffix[MAXN],tmp[MAXN],f[MAXN],g[MAXN];
    int cnt,n;
    vector<int>w[MAXN];
    ll Abs(ll x){
    	return x>0?x:-x;
    }
    int main(){
    	//为了方便处理,把b序列的左边加一个-INF,右边加一个+INF。
    	scanf("%d",&n);b[0]=-INF;
    	memset(g,0x3f,sizeof(g));
    	for(int i=1;i<=n;i++) scanf("%lld",&a[i]),b[i]=a[i]-i; 
    	a[++n]=INF;b[n]=a[n]-n; 
    	for(int i=1,l;i<=n;i++){
    		l=upper_bound(tmp+1,tmp+cnt+1,b[i])-tmp;
    		tmp[f[i]=l]=b[i];	
    		if(l>cnt) cnt=l;
    	}
    	printf("%d
    ",n-cnt); 
    	w[0].push_back(0); g[0]=0;
    	for(int i=1;i<=n;i++){
    		w[f[i]].push_back(i);
    		for(int p=0,sz=w[f[i]-1].size();p<sz;p++){
    			int j=w[f[i]-1][p];
    			if(b[j]>b[i]) continue;
    			for(int k=j;k<=i;k++) prefix[k]=prefix[k-1]+Abs(b[k]-b[j]);
    			for(int k=i;k>=j;k--) suffix[k]=suffix[k+1]+Abs(b[i]-b[k]);
    			for(int k=j;k<=i;k++)
    				g[i]=min(g[i],g[j]+prefix[k]-prefix[j]+suffix[k+1]-suffix[i]);
    		}
    	}
    	printf("%lld",g[n]);
    	return 0;
    }
  • 相关阅读:
    随堂笔记 17day
    随堂笔记16day
    随堂笔记day15 python
    随堂笔记14day python
    随堂笔记12day python
    随堂笔记python 11day 补
    java
    微信小程序入门
    #JS# 如何判断一个字符串是否为日期格式
    JS如何按时间粒度获取date的时间差
  • 原文地址:https://www.cnblogs.com/zj75211/p/7689355.html
Copyright © 2020-2023  润新知