- 又来水博客了……
- 上午
- 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; }