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("\n");
}
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",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;
}
Do not go gentle into that good night.
Rage, rage against the dying of the light.
————Dylan Thomas

浙公网安备 33010602011771号