UOJ Logo heqingyu的博客

博客

普转提训练Week12

2018-06-05 22:08:28 By heqingyu

这周比赛十分弱鸡,题目难度比较高,所以分数比较绝望

T2:

发现两个字符串的本质是相同的

于是可以把他们都用“最小表示法”来表示出来

然后判断是否相同即可

code:

#include<bits/stdc++.h>
using namespace std;
char a[1000010],b[1000010];
int solve(char s[]){
    int len=strlen(s+1);
    for(int i=1;i<=len;i++) s[len+i]=s[i];
    int i=1,j=2,k=0;
    while(i<=len&&j<=len){
        for(k=0;k<=len&&s[i+k]==s[j+k];k++);
        if(k==len) break;
        if(s[i+k]>s[j+k]){
            i+=k+1;
            if(i==j) i++;
        }
        else{
            j+=k+1;
            if(i==j) j++;
        }
    }
    return min(i,j);
}
int main(){
    scanf("%s%s",a+1,b+1);
    int la=strlen(a+1),lb=strlen(b+1);
    int x=solve(a),y=solve(b);
    a[la+x]=b[lb+y]=0;
    if(la==lb&&!strcmp(a+x,b+y)){
        puts("Yes");
        printf("%s",a+x);
    }
    else puts("No");
    return 0;
}

T3:

我们发现2^k步之后,每个硬币的状态之和离他2^k远的硬币有关

于是用一种二进制拆解的方法,把T拆开,然后做,复杂度O(nlog(T))

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int f[2][1000010];
int x=1,y,n,flag;
ll T;
ll get(ll mx){
    ll ans=2;
    while(ans<=mx) ans<<=1;
    ans>>=1;
    return ans;
}
int main(){
    scanf("%d%lld",&n,&T);
    for(int i=0;i<n;i++)scanf("%d",&f[0][i]);
    flag=T%2;
    T>>=1;
    while(T){
        ll t=get(T);
        T-=t;
        for(int i=0;i<n;i++)f[x][i]=(f[y][(i+n-t%n)%n]!=f[y][(i+t)%n])+1;
        swap(x,y);
    }
    if(flag){
        f[y][n]=f[y][0];
        for(int i=0;i<n;i++) printf("0 %d ",(f[y][i]!=f[y][i+1])+1);
    }
    else for(int i=0;i<n;i++) printf("%d 0 ",f[y][i]);
    return 0;
}

T4:

玄学做法

code:

#include<bits/stdc++.h>
using namespace std;
int n,b;
int a[100010];
int ans,pos,cnt;
int l[200010],r[200010];
int main(){
    scanf("%d%d",&n,&b);
    l[n]=r[n]=1;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        if(a[i]==b) pos=i,a[i]=0;
        if(a[i]<b) a[i]=-1;
        if(a[i]>b) a[i]=1; 
    }
    cnt=n;
    for(int i=pos-1;i>=1;i--) cnt+=a[i],l[cnt]++;
    cnt=n;
    for(int i=pos+1;i<=n;i++) cnt+=a[i],r[cnt]++;
    for(int i=0;i<=2*n;i++) ans+=l[i]*r[2*n-i];
    printf("%d",ans);         
    return 0;
}

T5:

我们可以利用DP的思想,设l[i]表示i左边第一个比h[i]高的下标

r[i]表示i右边第一个比h[i]高的下标

如果两个点可以看到,那么其中必定有一个矮的(或者同高)

于是我们枚举一个i,然后发现l[i]-r[i]这段区间中i只能够和l[i]与r[i]连接,并且不能连接到l[i]-r[i]以外

注意要判断l[i]是否和r[i]相等

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,a[1000010];
int pos,b[1000010];
int same[1000010],maxn=-1;
int r[1000010],l[1000010];
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
        if(a[i]>maxn){
            maxn=a[i];
            pos=i;
        }
    }
    for(int i=0;i<n;i++) b[i]=a[(pos+i)%n];
    b[n]=a[pos];
//    for(int i=0;i<n;i++) printf("%d ",b[i]);
    for(int i=n-1;i>=0;i--){
        r[i]=i+1;
        while(r[i]<n&&b[i]>b[r[i]]) r[i]=r[r[i]];
        while(r[i]<n&&b[i]==b[r[i]]){
            same[i]=same[r[i]]+1;
            r[i]=r[r[i]];
        }
    }
    for(int i=0;i<n;i++){
        l[i]=i-1;
        while(l[i]>0&&b[i]>=b[l[i]]) l[i]=l[l[i]];
    }
    ll ans=0;
    for(int i=0;i<n;i++){
        ans+=same[i];
        if(b[i]<b[0]){
            if(l[i]==0&&r[i]==n) ans++;
            else ans+=2;
        }
    }
    printf("%lld",ans);
    return 0;
}

Week11训练总结

2018-05-28 13:22:58 By heqingyu

这次比赛zz的不行,本来预计100+100+15+15+100+100=430的。。

但是T2的错误和T5的错误实在是好笑。。。

T1:

模拟

code:

#include<bits/stdc++.h>
using namespace std;
int n,a[1010],b[1010];
char s1[1010],s2[1010];
int ans;
int main(){
    scanf("%d%s%s",&n,s1+1,s2+1);
    for(int i=1;i<=n;i++){
        a[i]=s1[i]-'0';
        b[i]=s2[i]-'0';
        int x=abs(a[i]-b[i]);
//        printf("%d %d\n",a[i],b[i]);
        int y=10-a[i]+b[i];
        int z=10-b[i]+a[i];
        ans+=min(x,min(y,z));
    }
    printf("%d",ans);
    return 0;
}

T2:

没有上司的舞会改版。。但是要注意map细节。。。

使用map来离散这些人名,然后跑树形DP即可

code:

#include<bits/stdc++.h>
using namespace std;
int n,root,g[100010];
int f[100010][2],cnt;
string x,y;
map<string,int> mp;
vector<int> edge[100010];
void tree_dp(int u,int pre){
    for(int i=0;i<edge[u].size();i++){
        int v=edge[u][i];
        if(v==pre) continue;
        tree_dp(v,u);
        f[u][0]+=max(f[v][0],f[v][1]);
        f[u][1]+=f[v][0];
    }
    return;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        cin>>x;
        if(!mp[x]) mp[x]=++cnt;
        scanf("%d",&f[mp[x]][1]);
        cin>>y;
        if(!mp[y]) mp[y]=++cnt;
//        f[mp[y]][1]=temp;
        if(y=="NOBODY") root=mp[x];
        g[mp[x]]=1;
        edge[mp[x]].push_back(mp[y]);
        edge[mp[y]].push_back(mp[x]);
    }
//    for(int i=1;i<=cnt;i++)if(!g[i]) root=i;
    tree_dp(root,0);
    printf("%d",max(f[root][1],f[root][0]));
    return 0;
}

T3:

不会。。打个15分的骗分吧。。

T4:

60分的算法有些问题。。我的只有50分。。。

正常的奇偶性贪心是错误的

@larryzhong @wujinzhao @siyuan 求教各位大佬哪里错了。。

code:

#include<bits/stdc++.h>
using namespace std;
int f[10010][10010][2][2];
int n,a[200010],m;
int main(){
    scanf("%d%d",&n,&m);
    if(m>n/2){
        puts("Error!");
        return 0; 
    }
//    memset(f,-127,sizeof(f));
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    f[1][1][1][1]=a[1];
    for(int i=2;i<=n;i++)
        for(int j=1;j<=m;j++){
            f[i][j][0][0]=max(f[i-1][j][0][0],f[i-1][j][1][0]);
            f[i][j][0][1]=max(f[i-1][j][0][1],f[i-1][j][1][1]);
            f[i][j][1][0]=f[i-1][j-1][0][0]+a[i];
            if(i!=2) f[i][j][1][1]=f[i-1][j-1][0][1]+a[i];
        }
//    for(int i=1;i<=n;i++)
//        for(int j=1;j<=m;j++)printf("%d %d %d %d\n",i,j,f[i][j][0],f[i][j][1]);
    int ans=0;
    ans=max(max(f[n][m][1][0],f[n][m][0][0]),f[n][m][0][1]);
    printf("%d",ans);
    return 0;
}

T5:

一道图论题。。。

发现最后求的答案肯定位于原图最小生成树上(环切性质)

先处理处最小生成树,然后每次做LCA求一下即可

code:

#include<bits/stdc++.h>
#define pb push_back
#define mk make_pair
#define fi first
#define se second
using namespace std;
struct node{
    int x,y,z;
}edge[100010];
int fa[100010];
int n,m,q;
int u,v,ans;
int f[100010][30],g[100010][30];
int dep[100010];
vector<pair<int,int> > e[100010];
int cmp(node a,node b){return a.z<b.z;}
int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
void dfs(int u,int d,int pre,int cnt){
    f[u][0]=pre;
    g[u][0]=cnt;
    dep[u]=d;
    for(int i=0;i<e[u].size();i++){
        int v=e[u][i].fi;
        if(v==pre) continue;
        if(dep[v]) continue;
        dfs(v,d+1,u,e[u][i].se);
    }
    return;
}
int main(){
//    memset(g,127,sizeof(g));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z);
    sort(edge+1,edge+m+1,cmp);    
    int sum=0;
    for(int i=1;i<=m;i++){
        if(sum==n-1) break;
        int fx=getfa(edge[i].x),fy=getfa(edge[i].y);
        if(fx!=fy){
            e[edge[i].x].pb(mk(edge[i].y,edge[i].z));
            e[edge[i].y].pb(mk(edge[i].x,edge[i].z));
            fa[fx]=fy;
            sum++;
        }
    }
    scanf("%d",&q);
    dfs(1,1,0,0);
    for(int j=1;j<=25;j++)
        for(int i=1;i<=n;i++)
            f[i][j]=f[f[i][j-1]][j-1],g[i][j]=max(g[i][j-1],g[f[i][j-1]][j-1]);
    for(int ij=1;ij<=q;ij++){
        scanf("%d%d",&u,&v);
        ans=0;
        if(dep[u]<dep[v]) swap(u,v);
        for(int i=25;i>=0;i--)
            if(dep[u]>=dep[v]+(1<<i)) ans=max(ans,g[u][i]),u=f[u][i];
        if(u==v){printf("%d\n",ans);continue;}
        for(int i=25;i>=0;i--)
            if(f[u][i]!=f[v][i]){
                ans=max(ans,max(g[u][i],g[v][i]));
                   u=f[u][i];v=f[v][i];
            }
        ans=max(ans,max(g[u][0],g[v][0]));
        printf("%d\n",ans);
    }
    return 0;
}

T6:

发现每次移动其实不管移动多少,我们只关心前缀最小和和后缀最小和

于是枚举k,然后判断是否可行即可

code:

#include<bits/stdc++.h>
using namespace std;
int n,ans;
int a[1000010],b[1000010];
int c[1000010],d[1000010];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    int tot=a[1];b[1]=a[1];
    for(int i=2;i<=n;i++){
        tot+=a[i];
        b[i]=min(tot,b[i-1]);
    }
    c[n]=a[n],d[n]=a[n];
    for(int i=n-1;i>=1;i--){
        c[i]=min(a[i],c[i+1]+a[i]);
        d[i]=d[i+1]+a[i];
    }
    for(int i=1;i<=n;i++)
        if(c[i]>=0&&d[i]+b[i]>=0) ans++;
    printf("%d",ans);
    return 0;
}

Week10训练总结

2018-05-20 21:40:04 By heqingyu

这周成绩还算可以,但是仍有些遗憾。。。

本来D题肯定可以做出来的,但是由于水群过度+混迹B站Z站没怎么好好想。。。

T1:

水题,n^2枚举即可,(HuHao状压真的♂骚)

code:

#include<bits/stdc++.h>
using namespace std;
int n,a[110][110];
int ans,b[110];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%d",&a[i][j]);
    for(int i=1;i<=n;i++){
        int flag=1;
        for(int j=1;j<=n;j++)if(a[i][j]==1||a[i][j]==3) flag=0;
        if(flag) b[++ans]=i;
    }
    printf("%d\n",ans);
    for(int i=1;i<=ans;i++)printf("%d ",b[i]);
    return 0;
}

T2:

在吴教练眼中的“小学数学题+DP”什么不存在的吧。。。

式子什么的不会推,没有经验,多做做题吧。。(沉迷枚举,DP基本没做过、、)

我们设f[i][j][k]表示分别剩下i,j,k个人的概率

然后发现f[i-1][j][k]和f[i][j-1][k]和f[i][j][k-1]都可以继承这个值,但是要除以一个概率

最后统计一下答案即可

code:

#include<bits/stdc++.h>
using namespace std;
int a,b,c;
double f[110][110][110];
double ans1,ans2,ans3;
int main(){
    scanf("%d%d%d",&a,&b,&c);
    f[a][b][c]=1;
    for(int i=a;i>=1;i--)
        for(int j=b;j>=1;j--)
            for(int k=c;k>=1;k--){
                double cnt=i*j+j*k+i*k;
                f[i-1][j][k]+=f[i][j][k]*(i*k/cnt);            
                f[i][j-1][k]+=f[i][j][k]*(i*j/cnt);            
                f[i][j][k-1]+=f[i][j][k]*(j*k/cnt);            
            }
    for(int i=0;i<=a;i++)
        for(int j=0;j<=b;j++)
            ans1+=f[i][j][0];
    for(int i=0;i<=a;i++)
        for(int j=0;j<=c;j++)
            ans2+=f[i][0][j];
    for(int i=0;i<=b;i++)
        for(int j=0;j<=c;j++)
            ans3+=f[0][i][j];
    printf("%.9lf %.9lf %.9lf",ans1,ans3,ans2);
    return 0;
}

T3:

这道题表示只会nlogn算法。。。太菜了啊。。。

模拟交换,然后用树状数组做一遍即可。。。

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,a,mx,b;
int bel[10000010];
int c[10000010];
ll ans;
void swap(int &x,int &y){int t=x;x=y;y=t;return;}
int read(){
    int x=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
int lowbit(int x){return x&-x;}
void change(int x){
//    if(!x) x=1;
    while(x<=mx){
        c[x]++;
        x+=lowbit(x);
    }
    return;
}
ll query(int x){
    ll cnt=0;
    while(x){
        cnt+=c[x];
        x-=lowbit(x);
    }
    return cnt;
}
int main(){
    n=read();
    mx=-1;
    for(int i=1;i<=1e7;i++)bel[i]=i;
    for(int i=1;i<=n;i++){
        a=read();b=read();
        swap(bel[a],bel[b]);
        mx=max(mx,max(a,b));
    }
    for(int i=1;i<=mx;i++){
        change(bel[i]);
        ans+=i-query(bel[i]);
    }
//    for(int i=1;i<=mx;i++)printf("%d ",bel[i]);
//    puts("");
    printf("%lld",ans);
    return 0;
}

T4:

首先发现首尾两棵树肯定分别向左向右倒,然后再考虑每棵树:

得到一个贪心策略:如果可以往左放就往左放(之前已经计算过,显然比右边优),如果可以往右放再往右放(优先左)

code:

#include<bits/stdc++.h>
using namespace std;
int n,x[1000010],h[100010];
//struct node{
//    int l,r,p;
//}a[200010];
int ans;
//bool cmp(node u,node v){return u.r<v.r;}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d%d",&x[i],&h[i]);
    x[n+1]=1e10;
    for(int i=2;i<=n;i++){
        if(x[i]-x[i-1]>h[i]) ans++;
        else if(x[i+1]-x[i]>h[i]){
            x[i]+=h[i];
            ans++;
        }
    }
    printf("%d",++ans);
    return 0;
}

T5:

第一眼看到就知道是记忆化搜索+玄学猜结论。。。

但是我就是要写BFS+队列(傲娇)

最后判断一下终点的情况:

1.可以直接掉下去,那么如果终点周围有联通的点就可以了

2.不可以直接掉,要求终点周围有两个以上的联通的点

code:

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
int n,m,sx,sy;
int ex,ey,s,t;
char mp[510][510];
int dis[510][510];
pair<int,int> que[100010];
vector<pair<int,int> > edge[510][510];
int nxt[5][2]={0,0,-1,0,1,0,0,-1,0,1};
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%s",mp[i]+1);
    scanf("%d%d%d%d",&sx,&sy,&ex,&ey);
    memset(dis,127,sizeof(dis));
    dis[sx][sy]=0;
    que[++t]=make_pair(sx,sy);
    while(s<t){
        int ux=que[++s].fi;
        int uy=que[s].se;
        for(int i=1;i<=4;i++){
            int tx=ux+nxt[i][0];
            int ty=uy+nxt[i][1];
            if(mp[tx][ty]=='.'&&dis[tx][ty]){
                dis[tx][ty]=0;
                que[++t]=make_pair(tx,ty);
            }
        }
    }
    int cnt=0;
    for(int i=1;i<=4;i++){
        int tx=ex+nxt[i][0];
        int ty=ey+nxt[i][1];
        if(mp[tx][ty]=='.') cnt++;
    }
    if(!dis[ex][ey]&&cnt>1) puts("YES");
    else{
        if(mp[ex][ey]=='X'){
            for(int i=1;i<=4;i++){
                int tx=ex+nxt[i][0];
                int ty=ey+nxt[i][1];
                if(!dis[tx][ty]) cnt++;
            }
            if(cnt) puts("YES");
            else puts("NO");
        }
        else puts("NO");
    }
    return 0;
}

T6:

发现是个贪心题,但是要注意一点:

假如当前顾客已经不满意了,那么就不要给他服务了(滚蛋吧)(服务态度极差诶),因为他后面也不可能满意了。。

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,t[100010];
ll ans,now;
int main(){
//    freopen("A.in","r",stdin);
//    freopen("A.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&t[i]);
    sort(t+1,t+n+1);
    for(int i=1;i<=n;i++){
//        printf("%d\n",now);
        if(now<=t[i]) ans++;
        else continue;
        now+=t[i];
    }
    printf("%d",ans);
    return 0;
}

Week 7总结

2018-04-24 06:58:01 By heqingyu

这周状态不太好,题目也比较毒瘤。。。

本来以为能400++,因为我打了四题的标算(T4模拟也算)+2题骗分

然而现实十分残酷,T3玄学(单项边,双向边。。。),T4玄学(模拟也能错我也很绝望)

T1:

本来听说是水题。。。然后就想多了想到状压去了。。。

发现每行翻转两次其实是一样的效果,于是只需翻转一次即可

大概就是说先2^n搜索出每一行是否需要翻转,然后再判断m列是否要翻转,更新答案

code:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[20][20];
int ans1=1e9,ans2=1e9;
char s[110];
int main(){
    scanf("%d%d",&n,&m);
      for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        for(int j=1;j<=m;j++) a[i][j]=s[j]-'0'; 
    }
      for(int s=0;s<(1<<n);s++){
        int c1=0;
        for(int i=1;i<=n;i++)
              if(s>>(i-1)&1){
                for(int j=1;j<=m;j++)
                  a[i][j]^=1;
                c1++;
              }
        int tot=0;
        for(int j=1;j<=m;j++){
            int c=0;
            for(int i=1;i<=n;i++) c+=a[i][j];
              if(n-c<c) c1++,tot+=n-c;
              else tot+=c;
        }
        if(!tot) ans1=min(ans1,c1);
        else ans2=min(ans2,tot);
        for(int i=1;i<=n;i++)
              if(s>>(i-1)&1) for(int j=1;j<=m;j++) a[i][j]^=1;
      }
    if(ans1==1e9) printf("%d",ans2);
    else printf("%d",ans1);
     return 0;
}

T2:

贪心水题,sort一遍模拟计算答案

注意long long。。。。

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,a[1000010];
ll ans;
bool cmp(ll x,ll y){return x>y;}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    sort(a+1,a+n+1,cmp);
    for(ll i=2;i<=n;i++)ans+=(ll)((i-1)*a[i]);
    printf("%lld",ans);
    return 0;
}

T3:

被单双向边搞烦了、、干脆放弃希望。。。

发现其实是让我们求出1~n的一个全排列后计算答案

然后这里介绍一个STL:

next_permutation!!!

语法:next_permutation(c+1,c+n+1),会生成一个大于当前c的全排列,如果没有就返回false

code:

#include<bits/stdc++.h>
using namespace std;
int n,m,x,y;
int a[110][110],b[110][110];
int bel[110],ans;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        a[x][y]++;
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        b[x][y]++;
    }
    int tot=1;
    for(int i=1;i<=n;i++)bel[i]=i;
    while(next_permutation(bel+1,bel+n+1)){    
        if(tot) for(int i=1;i<=n;i++)bel[i]=i;
        int cnt=0;tot=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                cnt+=min(a[i][j],b[bel[i]][bel[j]]);
        ans=max(ans,cnt);
    }
    printf("%d",ans);
    return 0;
}

T4:

这道题我直接上模拟了。。。根本不想打什么标算。。。

code:

#include<bits/stdc++.h>
using namespace std;
int a[6000010],n,m;
int cnt;
char ch,b[5000010];
int main(){
    scanf("%d%d",&n,&m);
    getchar();
    for(int i=1;i<=n;i++){
        ch=getchar();
        a[i]=ch-'0';
    }
    scanf("%s",b+1);
    for(int i=1;i<=m;i++){
        int now=n;
        if(b[i]=='+'){
            a[now]++;
            while(a[now]==2){
                a[now]=0;
                a[--now]++;
            }
        }
        if(b[i]=='-'){
            if(a[now]) a[now]=0;
            else while(!a[now]){
                a[now]=1;
                now--;
            }
            a[now]=0;
        }
        if(b[i]=='*') n++,a[n]=0;
        if(b[i]=='/') n--;
    }
    for(int i=1;i<=n;i++)putchar(a[i]+'0');
    return 0;
}

T5:

考虑每个点只有奇偶性重要,然后考虑每个点对答案奇偶性的贡献

发现除了对角线上的点,每个点都会被计算两边,也就是对奇偶性无影响。。。

于是只需要统计对角线的奇偶性即可

code:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int ans,x,y;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j){
            scanf("%d",&x);
            if(i==j&&x) ans^=1;
        }
    while(m--){
        scanf("%d",&x);
        if(x==1||x==2) scanf("%d",&y),ans^=1;
        else printf("%d",ans);
    }
    return 0;
}

T6:

这题有两个做法,dp太玄学根本不会,。。采用了雷霸霸的做法。。。

首先找规律,竖着刷当然是从头刷到尾最优

横着刷的话就是从最左边刷到最右边。。。

于是想到一个分治的做法,就是对于一段区间[l,r],竖着刷最少次数肯定是r-l+1

那么横着刷就是这段区间的最小高度次数+除去下面一段的最小次数

这时候后者就可以分治递归下去了。。。

code:

#include<bits/stdc++.h>
using namespace std;
int ans,h[50010],n;
int solve(int l,int r,int last){
    if(l==r) return 1;
    int cnt=0,mn=1e9+1;
    for(int i=l;i<=r;i++) mn=min(mn,h[i]);
    for(int i=l;i<=r;i++){
        if(h[i]==mn) continue;
        int j;
        for(j=i;j<=r;j)if(h[++j]==mn) break; 
        cnt+=solve(i,j-1,mn);
        i=j-1;
    }
    return min(cnt+mn-last,r-l+1);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&h[i]);
    ans=solve(1,n,0);
    printf("%d",ans);
    return 0;
}

Week 6总结

2018-04-15 19:57:09 By heqingyu

这周终于上300分了。。然而其实400是挺稳的。。。

犯了一些玄学错误(递归爆栈,搜索变贪心。。。。)

本来预计得分是100+100+100+100+30+10=440的。。。@Siyuan最强啦

T1:

洛谷原题,USACO的佬(没有打错)题了。。。

枚举最小山峰的高度,然后把所有山都修建成符合条件的区间内的山即可

code:

#include<bits/stdc++.h>
using namespace std;
int n,a[1010];
int ans=1e9,cnt;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int h=0;h<=100;h++){
        cnt=0;
        for(int i=1;i<=n;i++){
            if(a[i]>h) cnt+=(a[i]-h)*(a[i]-h);
            if(a[i]<h-17) cnt+=(h-17-a[i])*(h-17-a[i]);
        }
        ans=min(ans,cnt);
    }
    printf("%d",ans);
    return 0;
}

T2:

洛谷原题,USACO佬题(还是没有打错。。)。。。

首先考虑模拟,然后把时间和距离分开来处理就好了呀。。。

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;  
char ch;  
double last,ans;
int d[10010],t[10010];  
int n,x,now=1;
int nt,nd,cnt=1;  
int main(){  
    d[++nd]=0;  
    d[++nd]=1000;  
    scanf("%d",&n);  
    for(int i=1;i<=n;i++){  
        getchar();    
        scanf("%c%d",&ch,&x);  
        if(ch=='T') t[++nt]=x;  
        else d[++nd]=x;  
    }  
    sort(d+1,d+nd+1);  
    sort(t+1,t+nt+1);  
    for(int i=1;i<nd;i++){  
        last=d[i];  
        while(last<d[i+1]&&now<=nt&&ans+(d[i+1]-last)*cnt>t[now]){  
            last+=(t[now]-ans)/cnt;  
            cnt++;  
            ans=t[now++];  
        }  
        ans+=(d[i+1]-last)*cnt;  
        cnt++;  
    }  
    printf("%.0lf\n",ans);  
    return 0;
}

T3:

4^12次的爆搜,然而我却忘记了每组都要3头牛。。。

后来想起来,但是想到这样会导致答案不优,就懒得改了。。。(woc懒癌晚期了。。)

然而发现这样是错的。。。

code:

#include<bits/stdc++.h>
using namespace std;
int b[5],a[20];
int ans=1e9;
void dfs(int dep,int cnt1,int cnt2,int cnt3,int cnt4){
    if(dep>12){
        ans=min(ans,max(max(cnt1,cnt2),max(cnt3,cnt4))-min(min(cnt1,cnt2),min(cnt3,cnt4)));
        return;
    }
    if(b[1]<3){
        b[1]++;
        dfs(dep+1,cnt1+a[dep],cnt2,cnt3,cnt4);
        b[1]--;
    }
    if(b[2]<3){
        b[2]++;
        dfs(dep+1,cnt1,cnt2+a[dep],cnt3,cnt4);
        b[2]--;
    }
    if(b[3]<3){
        b[3]++;
        dfs(dep+1,cnt1,cnt2,cnt3+a[dep],cnt4);
        b[3]--;
    }
    if(b[4]<3){
        b[4]++;
        dfs(dep+1,cnt1,cnt2,cnt3,cnt4+a[dep]);
        b[4]--;
    }
    return;
}
int main(){
    for(int i=1;i<=12;i++)scanf("%d",&a[i]);
    dfs(1,0,0,0,0);
    printf("%d",ans);
    return 0;
}

T4:

一开始想到最小生成树。。。然后赛后验证发现爆零。。(还好没有写。。)

想到了二分+DFS来判断最小答案。。。

然而痛苦地爆栈我也很绝望。。。。(早知道写queue的广搜了。。。)

code:

#include<bits/stdc++.h>
#define pii pair<int,int> 
#define fi first
#define se second
#define pb push_back
#define mk make_pair
using namespace std;
int m,n,ans,tot,vis[510][510];
vector<pii > mp;
int h[510][510],x,t;
void dfs(int x,int y,int tot){
    if(x<1||x>n||y<1||y>m) return;
    if(vis[x][y]) return;
    vis[x][y]=1;
    if(abs(h[x-1][y]-h[x][y])<=tot) dfs(x-1,y,tot);
    if(abs(h[x+1][y]-h[x][y])<=tot) dfs(x+1,y,tot);
    if(abs(h[x][y-1]-h[x][y])<=tot) dfs(x,y-1,tot);
    if(abs(h[x][y+1]-h[x][y])<=tot) dfs(x,y+1,tot);
    return;
}
bool check(int d){
    dfs(mp[0].fi,mp[0].se,d);
    bool flag=true;
    for(int i=0;i<mp.size();i++)if(!vis[mp[i].fi][mp[i].se]) flag=false;
    return flag;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&h[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            scanf("%d",&x);
            if(x==1) mp.pb(mk(i,j));
        }
    int l=0,r=1e9;
    while(l<=r){
        int mid=l+r>>1;
        memset(vis,0,sizeof(vis));
        if(check(mid)) r=mid-1,ans=mid;
        else l=mid+1;
    }
    printf("%d",ans);
    return 0;
}

T5:

这题的线段树+扫描线实在是太♂骚了。。。。

于是只打了个n^3的暴力,骗了30分。。。。

code:

#include<bits/stdc++.h>
using namespace std;
int mp[1010][1010];
int n,a,x,b,y;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d%d",&x,&y,&a,&b);
        x+=200;
        y+=200; 
        for(int j=x;j<=x+a-1;j++)
            for(int ij=y;ij<=y+b-1;ij++)
                mp[j][ij]=1;
    }
    int ans=0;
    for(int i=0;i<=1000;i++)
        for(int j=0;j<=1000;j++)
            ans+=mp[i][j];
    printf("%d",ans);
    return 0;
}

T6:

这题标算很♂玄学。。。

于是打了个@Siyuan教我的骗分

就是输出样例和10.。。。(样例特判。。)

竟然能拿40分!!!太神了!!!比暴力还高!!!!

code:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int main(){
    scanf("%d%d",&n,&m);
    if(n==3&&m==6) puts("3");
    else puts("10");
    return 0;
}
heqingyu Avatar