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;
}

Day5

2018-04-07 19:41:14 By heqingyu

这次比赛估计排名和我水平差不多了,。,

%%%XRY%%%DSY

T1:

暴力出奇迹!!

code:

#include<bits/stdc++.h>
using namespace std;
int x,now;
bool check(int tot){
    int cnt1=tot%10+(tot/10)%10+(tot/100)%10;
    int cnt2=(tot/1000)%10+(tot/10000)%10+(tot/100000)%10;
    return cnt1==cnt2;
}
int main(){
    scanf("%d",&x);
    now=x+1;
    while(now<1e7){
        if(check(now)){printf("%d",now);return 0;}
        now++;
    }
    return 0;
}

T2:

比赛时第一眼就看出了是个贪心,肯定是从min走到max。。。

但是还是打了个最小生成树对拍一下。。。。

code:

#include<bits/stdc++.h>
using namespace std;
int t,n,x[110],ans;
int main(){
    scanf("%d",&t);
    while(t--){
        ans=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&x[i]);
        sort(x+1,x+n+1);
        for(int i=1;i<n;i++)ans+=abs(x[i+1]-x[i]);
        printf("%d\n",ans);
    }
    return 0;
}

T3:

我们记fa[i]表示A数组中有无i的倍数,fb[i]表示B数组中有无i的倍数

然后我们枚举对于每个A,B,预处理出fa,fb数组(sqrt(A)的复杂度)

接着枚举i,如果fa[i]与fb[i]同时为true时,说明i满足gcd,更新答案

最后O(n)扫一遍,求出最大的x+y即可

code:

#include<bits/stdc++.h>
using namespace std;
int n,A[500010],B[500010],fa[1000010],fb[1000010];
int ans;
void solve1(int x){
    for(int i=1;i<=sqrt(x);i++){
        int k=x/i;
        if(x%i==0){fa[i]++;fa[k]++;}
    }
    return;
}
void solve2(int x){
    for(int i=1;i<=sqrt(x);i++){
        int k=x/i;
        if(x%i==0){fb[i]++;fb[k]++;}
    }
    return;
}
int tot,ansa,ansb;
int main(){
    freopen("gcd.in","r",stdin);
    freopen("gcd.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&A[i]),solve1(A[i]);
    for(int i=1;i<=n;i++)scanf("%d",&B[i]),solve2(B[i]);
    for(int i=1000000;i>=1;i--)if(fa[i]&&fb[i]){tot=i;break;}
    for(int i=1;i<=n;i++)if(A[i]%tot==0) ansa=max(ansa,A[i]);
    for(int i=1;i<=n;i++)if(B[i]%tot==0) ansb=max(ansb,B[i]);
    printf("%d",ansa+ansb);
    return 0;
}

T4:

考虑品牌数量极少,可以直接二分+暴力check

首先把物品按价格sort,接下来设s1[i]表示比i物品便宜的,品牌为1的物品数量,其他以此类推

然后二分物品标号,check的话就把所有物品标号<=mid且品牌被XXX所喜爱的加起来

如果数量小于k,那么说明需要价格更高的,l=mid+1;

否则的话就r=mid-1;

code:

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
int n,q,d,k,brand[6];
struct node{
    int b,p;
}a[100010];
int s1[100010],s2[100010],s3[100010],s4[100010],s5[100010];
bool cmp(node x,node y){return x.p<y.p;}
int check(int x){
    int cnt=0;
    for(int i=1;i<=d;i++){
        if(brand[i]==1) cnt+=s1[x];
        if(brand[i]==2) cnt+=s2[x];
        if(brand[i]==3) cnt+=s3[x];
        if(brand[i]==4) cnt+=s4[x];
        if(brand[i]==5) cnt+=s5[x];
    }
    return cnt;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i].b);
    for(int i=1;i<=n;i++)scanf("%d",&a[i].p);
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++){
        if(a[i].b==1) s1[i]=s1[i-1]+1;else s1[i]=s1[i-1];
        if(a[i].b==2) s2[i]=s2[i-1]+1;else s2[i]=s2[i-1];
        if(a[i].b==3) s3[i]=s3[i-1]+1;else s3[i]=s3[i-1];
        if(a[i].b==4) s4[i]=s4[i-1]+1;else s4[i]=s4[i-1];
        if(a[i].b==5) s5[i]=s5[i-1]+1;else s5[i]=s5[i-1];
    }
    scanf("%d",&q);
    int ans=0;
    while(q--){
        ans=0;
        scanf("%d",&d);
        for(int i=1;i<=d;i++)scanf("%d",&brand[i]);
        scanf("%d",&k);
        if(check(n)<k){puts("-1");continue;}
        int l=1,r=n;
        ans=-1;
        while(l<=r){
            int mid=l+r>>1;
            if(check(mid)>=k) r=mid-1;
            else l=mid+1;
        }
        printf("%d\n",a[l].p);
    }
    return 0;
}

T5:

设f[i][j][k]表示A串前i个字符,B串前j个字符,LCS至少为K的最小代价

考虑对于i和j,有三种决策:

1.修改i,使其变成j,代价为i^j

2.对于第i个,跳过,不放入LCS

3.对于第j个,跳过,不放入LCS

code:

#include<bits/stdc++.h>
using namespace std;
int n,m,k,f[360][360][360];
char A[360],B[360];
int solve(int a,int b,int c){
    if(!c) return 0;
    if(!a||!b||!c) return 1e9;
    if(f[a][b][c]!=-1) return f[a][b][c];
    int cos=(A[a]-'a')^(B[b]-'a');
    return f[a][b][c]=min(cos+solve(a-1,b-1,c-1),min(solve(a-1,b,c),solve(a,b-1,c)));
}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    memset(f,-1,sizeof(f));
    scanf("%s%s",A+1,B+1);
    if(solve(n,m,k)==1e9) puts("-1");
    else printf("%d",solve(n,m,k));
    return 0;
}

T6:

这题的标算是线段树,没有敲。。敲了一个玄学暴力。。于是只讲做法。。。

发现如果x>=40,则x!%1e9==0

所以只需要考虑小于40的所有数,这些数用线段树来维护即可(说起来真简单)

暴力code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a[1000010],n,m,p,l,r;
ll jie[41];
const int mod=1e9;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    jie[0]=1;
    for(int i=1;i<=40;i++)jie[i]=(ll)jie[i-1]*i%mod;
    while(m--){
        ll cnt=0;
        scanf("%d%d%d",&p,&l,&r);
        if(p==1) for(int i=l;i<=r;i++) a[i]++;
        if(p==2) for(int i=l;i<=r;i++){cnt+=a[i]>40?0:jie[a[i]];cnt%=mod;}
        if(p==3) a[l]=r;
        if(p==2) printf("%lld\n",cnt);
    }
    return 0;
}

Day4总结

2018-04-06 23:20:30 By heqingyu

一个晚上熬夜连续写两篇博客,好累啊。。

再次%%%丁思源霸霸AKdalao Siyuan

T1:

听说是普及组T1难度。。。因为没开longlong只有60分很伤

就是枚举到sqrt(n)暴力找质因子。。。

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,t;
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%lld",&n);
        int flag=0;
        ll m=n;
        for(ll i=2;i<=sqrt(m);i++){
            while(n%i==0){
                printf("%lld ",i);
                n/=i;
                flag=1;
            }
        }
        if(n!=1) printf("%lld",n);
        puts("");
    }
    return 0;
}

T2:

又是一道大模拟。。。。

最近模拟越来越多了。。。

但是需要注意的是当n为奇数时最中间的一个数可能会变大。。。

再就是要加输出优化。。。

code:

#include<bits/stdc++.h>
using namespace std;
int n,a[1010][1010],x,cnt,tmp;
void print(int x){
    if(x<10) putchar(x+'0');
    else{
        print(x/10);
        putchar(x%10+'0');
    }
    return;
}
void solve(int now,int flag){
    if(flag==1){
        for(int i=now;i<=n-now+1;i++)a[now][i]=++cnt;
        for(int i=now+1;i<=n-now;i++)a[i][n-now+1]=++cnt;
        for(int i=n-now+1;i>=now;i--)a[n-now+1][i]=++cnt;
        for(int i=n-now;i>now;i--)a[i][now]=++cnt;
        tmp=cnt;
    }
    else{
        for(int i=now;i<=n-now+1;i++)a[i][now]=++cnt;
        for(int i=now+1;i<=n-now+1;i++)a[n-now+1][i]=++cnt;
        for(int i=n-now;i>=now;i--)a[i][n-now+1]=++cnt;
        for(int i=n-now;i>now;i--)a[now][i]=++cnt;
        tmp=cnt;
    }
    return ;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=(n+1)/2;i++){
        scanf("%d",&x);
        solve(i,x);        
    }
     if(n%2==1) a[(n+1)/2][(n+1)/2]=tmp-1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            print(a[i][j]),putchar(' ');
        puts("");
    }
    return 0;
}

T3:

多重背包模板。。。

首先讲讲二进制优化,就是把多重背包转换为01背包

当一个物品数量为s时,我们将它分解为2^0个,2^1……2^k个物品,最后还有s-1-2-4-……-2^k个物品

这样就可以保证若干个分解后的物品可以凑出1~s中的任意物品数量

别告诉我你不会01背包(反正我不会)

code:

#include<bits/stdc++.h>
using namespace std;
int n,v,w1,c1,m1;
int w[500010],c[500010],cnt;
int f[1000010];
int _pow(int x){
    if(!x) return 1;
    int cnt=1;
    for(int i=1;i<=x;i++)cnt*=2;
    return cnt;
}
void solve(int tot,int h,int va){
    int k=0;
    while(_pow(k)<=tot){
        tot-=_pow(k);
        w[++cnt]=h*_pow(k);
        c[cnt]=va*_pow(k);
        k++;
    }
    if(tot) w[++cnt]=tot*h,c[cnt]=tot*va;
    return;
}
int main(){
    scanf("%d%d",&n,&v);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&w1,&c1,&m1);
        solve(m1,w1,c1);
    }
    for(int i=1;i<=cnt;i++)
        for(int j=v;j>=w[i];j--)
            f[j]=max(f[j],f[j-w[i]]+c[i]);
    printf("%d\n",f[v]);
    return 0;
}

T4:

状压DP,没有敲(std把我吓到了。。)

设f[i][j][s]表示到达i,j,钥匙的状态为s(即01串)能否做到

然后玄学转移。。。

T5:

首先想到一个只有20分的hash:

对于每一个皇后,枚举他的攻击路径,将坐标hash一下后去重,计算答案

但是这样的空间复杂度是n*m的,会爆炸

于是我们想到可以分成每一行来做。。。

接下来设h[i]表示目前这一行第i个格子是否被覆盖

然后可以初始化t,表示这一行没有被覆盖的格子数,初识t=m

分两种情况:

1.如果该格被某个皇后横向攻击的话,那么整行都会被覆盖,t=0

2.如果一格之前没有被攻击过,但是现在被攻击了,t--

最后对于每一行ans+=t

然后这里顺便打了个时间戳优化。。。

code:

#include<bits/stdc++.h>
using namespace std;
int hash[100010],n,m,k,ans,x[100010],y[100010];
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=k;i++)scanf("%d%d",&x[i],&y[i]);
    for(int i=1;i<=n;i++){
        int t=m;
        for(int j=1;j<=k;j++){
            if(x[j]==i){
                t=0;
                break;
            }
            if(hash[y[j]]!=i){
                hash[y[j]]=i;
                t--;
            }
            if((i+y[j]-x[j])>0&&(i+y[j]-x[j])<=m&&hash[i+y[j]-x[j]]!=i){
                hash[i+y[j]-x[j]]=i;
                t--;
            }
            if((y[j]+x[j]-i)>0&&(y[j]+x[j]-i)<=m&&hash[y[j]+x[j]-i]!=i){
                hash[y[j]+x[j]-i]=i;
                t--;
            }
        }    
        ans+=t;
    }
    printf("%d",ans);
    return 0;
}

T6:

要联通且边数最小那么肯定是一棵树。。

首先想到一个60分做法:

将边按边权sort一遍后,枚举最小边,使用并查集判断联通

一直往边权大的方向枚举,直到联通,更新答案,复杂度O(nm)

接下来是玄学标算,我也不知道为什么是对的。。

每次从上次计算完答案的区间起点开始,一直向上找联通块

找到之后从联通块中最大的边开始往下找联通块,不停地更新答案,复杂度O(n+m)(应该是的)

code:

#include<bits/stdc++.h>
#define se second
#define fi first
using namespace std;
int fa[100010],ans=1e9;
int n,m,a,b,c,l,r,cnt;
struct node{
    int x,y,z;
}edge[100010];
bool cmp(node x,node y){return x.z<y.z;}
int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
bool check(int now){
    int fx=getfa(edge[now].x),fy=getfa(edge[now].y);
    if(fx!=fy){
        cnt++;
        fa[fx]=fy;
        if(cnt==n-1){
            ans=min(ans,edge[r].z-edge[l].z);
            return true;
        }
    }
    return false;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&a,&b,&c);
        edge[i]=(node){a,b,c};
    }
    sort(edge+1,edge+m+1,cmp);
    while(1){
        for(int j=1;j<=n;j++)fa[j]=j;
        cnt=0;
        while(r<m)if(check(++r)) break;
        if(cnt!=n-1) break;
        cnt=0;
        l=r+1;
        for(int j=1;j<=n;j++)fa[j]=j;
        while(l>1)if(check(--l)) break;
        if(cnt!=n-1) break;
        r=l;
    }
    if(ans==1e9) puts("-1");
    else printf("%d",ans);
    return 0;
}

Day3总结

2018-04-06 22:54:48 By heqingyu

这次又是课后订正。。。。

我好菜啊QAQ,只做了400分

T1:

一道简单的方程题

设x=A-a,即利润

可得A-a+B-b-x=C-a-b

移项后得A+B-C=x

code:

#include<bits/stdc++.h>
using namespace std;
int T,a,b,c;
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&a,&b,&c);
        printf("%d\n",a+b-c);
    }
    return 0;
}

T2:

大模拟,首先计算出上下两面的表面积贡献为2nm

然后考虑左右前后四面,每一个凸出来的方块肯定会被计算一次表面积

于是枚举四个方向,加入答案即可

code:

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

T3:

首先考虑最短路,在无权图上的最短路肯定是BFS跑的最快啦(摘自PPT)

然后考虑计算路径,可以记录一个最短路的前缀,也就是从哪里来的,然后最后倒着回去输出即可

code:

#include<bits/stdc++.h>
#define pii pair<int,int> 
#define ll long long
#define fi first
#define se second
#define mk make_pair
using namespace std;
int n,sx,sy,ex,ey;
int h,t;
int nxt[6][2]={-2,-1,-2,1,0,2,2,1,2,-1,0,-2};
char name[6][10]={"UL","UR","R","LR","LL","L"};
int ans[500010],cnt;
int vis[210][210];
struct node{
    int x,y,las;
    int ans;
}q[500010];
bool check(int x,int y){return (!vis[x][y])&&(x>=0)&&(x<n)&&(y>=0)&&(y<n);}
int main(){
    scanf("%d%d%d%d%d",&n,&sx,&sy,&ex,&ey);
    q[++t]=(node){sx,sy,0,-1};
    vis[sx][sy]=1;
    while(h<t){
        pii now;
        now.fi=q[++h].x;
        now.se=q[h].y;
        for(int i=0;i<6;i++){
            int nx=now.fi+nxt[i][0];
            int ny=now.se+nxt[i][1];
            if(!check(nx,ny)) continue;
            vis[nx][ny]=1; 
            q[++t].las=h;
            q[t].ans=i;
            q[t].x=nx;
            q[t].y=ny;
            if(nx==ex&&ny==ey){
                while(q[t].las!=0){
                    ans[++cnt]=q[t].ans;
                    t=q[t].las;
                }
                printf("%d\n",cnt);
                while(cnt>0){printf("%s ",name[ans[cnt]]);cnt--;}
                return 0;
            }
        }
    }
    puts("Impossible");
    return 0;
}

T4:

一道显然的数学题(其实也可以用DP来做)

首先想到的就是DP,设:

f[i][0]表示第i个点放1的方案数

f[i][1]表示第i个点放x的方案数

f[i][2]表示第i个点既不放1也不放x的方案数

然后可以得到DP方程:

f[i][0]=f[i-1][1]+f[i-1][2]

f[i][1]=f[i-1][0]+f[i-1][2]

f[i][2]=f[i-1][2](k-3)+f[i-1][0](k-2)+f[i-1][1]*(k-2)

初始化f[1][0]=1。

但是有一种特殊情况,那就是x==1.这时候可以把x当做1来做,直接输出f[n-2][0](k-1)+f[n-2][2](k-1)

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll f[100010][3],n,k,x;
const ll mod=1e9+7;
int main(){
    scanf("%d%d%d",&n,&k,&x);
    f[1][0]=1;
    for(int i=2;i<n;i++){
        f[i][0]=(f[i-1][1]+f[i-1][2])%mod;
        f[i][1]=(f[i-1][0]+f[i-1][2])%mod;
        f[i][2]=(f[i-1][2]*(k-3)%mod+f[i-1][0]*(k-2)%mod+f[i-1][1]*(k-2)%mod)%mod;
    }
    if(x==1) printf("%lld",(f[n-2][0]+f[n-2][2])*(k-1)%mod); 
    else printf("%lld",(f[n-1][0]+f[n-1][2])%mod);
    return 0;
}

Day2总结

2018-04-02 12:36:11 By heqingyu

首先声明一下我现在正在订正上周的题。。。

这是赛后订正的于是没有任何反思总结。。

听说这次的前四题都很水、。。

然而丁思源爸爸照样AK虐全场%%%%

T1:

不得不说,真滴水。。。

考虑贪心,首先是最多人,那么就是每只XX(复制不了??)每次都只看见一个人,答案即为a+b

最少人的话就是每只XX都看见两个人,也就是max(a,b)

code:

#include<bits/stdc++.h>
using namespace std;
int a,b;
int main(){
    scanf("%d%d",&a,&b);
    printf("%d %d",max(a,b),a+b);
    return 0;
}

顺便说一下,以后我的代码会去掉freopen,以减少码量。。(懒死啦)

T2:

开个三个前缀和sa,sb,sc分别表示前i个数中ABC的数量,然后n^2枚举区间[l,r]判断

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll sa[1010],sb[1010],sc[1010],ans;
char s[1010];
int main(){
    scanf("%s",s+1);
    int n=strlen(s+1);
    for(int i=1;i<=n;i++){
        sa[i]=sa[i-1]+(s[i]=='A');
        sb[i]=sb[i-1]+(s[i]=='B');
        sc[i]=sc[i-1]+(s[i]=='C');
    }
    for(int l=1;l<=n;l++)
        for(int r=l;r<=n;r++)
            if(sa[r]-sa[l-1]==sb[r]-sb[l-1]&&sa[r]-sa[l-1]==sc[r]-sc[l-1]) ans++;
    printf("%lld",ans);
    return 0;
}

T3:

首先用筛法求出范围内的素数

接着枚举每个数,然后判断他的前后是否都是质数就好了

code:

#include<bits/stdc++.h>
using namespace std;
int p[1000010],l,r,ans;
void init(){
    p[0]=1;
    p[1]=1;
    for(int i=1;i<=sqrt(1000000);i++)
        if(!p[i]) for(int j=i*i;j<=1000000;j+=i) p[j]=1;
    return;
}
bool check(int x){
    for(int i=10;i<=1000000;i*=10)if((!p[x%i])&&(!p[x/i])) return true;
    return false;
}
int main(){
    scanf("%d%d",&l,&r);
    init();
    for(int i=l;i<=r;i++)if(check(i)) ans++;
    printf("%d",ans);
    return 0;
}

T4:

并查集的模板题,其实就是判断亲缘关系。。。

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int a,b,fa[100010],n,m,k;
int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&a,&b);
        int fx=getfa(a),fy=getfa(b);
        fa[fx]=fy;
    }
    for(int i=1;i<=k;i++){
        scanf("%d%d",&a,&b);
        int fx=getfa(a),fy=getfa(b);
        if(fx==fy) puts("I am SB!")    ;
        else puts("Sweet!");
    }
    return 0;
}

T5:

设f[i][j]表示i阶sam数的末尾为j的方案数,然后就可以把所有的f[k][i]加起来就可以得到答案了。。

注意判断前导0。。。

貌似标算需要矩阵乘法。。。

然后还有DSY爸爸的玄学倍增。。。

code:

#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;
ll ans,f[1000010][10],k;
int main(){
    scanf("%lld",&k);
    for(int i=1;i<=9;i++)f[1][i]=1;
    if(k==1){puts("10");return 0;}
    for(ll i=2;i<=k;i++)
        for(int j=0;j<=9;j++)
            for(int ij=-2;ij<=2;ij++){
                if(j+ij<0) continue;
                if(j+ij>9) continue;
                f[i][j]=(f[i][j]+f[i-1][j+ij])%mod;
            }
    for(int i=0;i<=9;i++)ans=(f[k][i]+ans)%mod;
    printf("%lld",ans);
    return 0;
}

T6:

这题只打了60分的暴力。。。

设s[i][j]表示矩阵[1,1][i,j]的数值和,然后暴力枚举一切、、

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,b,a,d,c,x;
ll s[1010][1010],ans;
ll solve(int x1,int y1,int x2,int y2){
    ll ans=s[x2][y2];
    ans-=s[x2][y1-1];
    ans-=s[x1-1][y2];
    ans+=s[x1-1][y1-1];
    return ans;
}
int main(){
    scanf("%d%d%d%d%d%d",&m,&n,&b,&a,&d,&c);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            scanf("%d",&x);
            s[i][j]=s[i-1][j]+s[i][j-1]+x-s[i-1][j-1];
        }
    for(int x1=1;x1+a-1<=n;x1++)
        for(int y1=1;y1+b-1<=m;y1++){
            ll cnt=solve(x1,y1,x1+a-1,y1+b-1);
            for(int x2=x1+1;x2+c<x1+a;x2++)
                for(int y2=y1+1;y2+d<y1+b;y2++){
                    ll tot=solve(x2,y2,x2+c-1,y2+d-1);
                    ans=max(ans,cnt-tot);
                }
        }
    printf("%lld",ans);
    return 0;
}

普及转提高模拟赛第8场(Day1)

2018-03-29 18:54:51 By heqingyu

这次博客写的好晚啊。。。。太忙了。。。太强了。。。

这次模拟赛打的有些不如意,本想着寒假做了挺多题,应该不错了,但是发现还是菜的一批

被C班霸霸们摁在鞋底摩擦啊。。。太强了。。。@丁思源

然后就是各种zz错误,可能是没有从OJ的从来不调代码这个习惯转成打OI赛制把。。。

T1:

最水的字符串判断题,但是最好不要用string,可能会爆。。。

然后zz的我把not成功的打成了no。。。。

code:

#include<bits/stdc++.h>
using namespace std;
int Q;
char s[10010];
int main(){
    freopen("acid.in","r",stdin);
    freopen("acid.out","w",stdout);
    scanf("%d",&Q);
    while(Q--){
        scanf("%s",s+1);
        int l=strlen(s+1);
        if(s[l-1]=='i'&&s[l]=='c'){
            if(s[1]=='h'&&s[2]=='y'&&s[3]=='d'&&s[4]=='r'&&s[5]=='o') puts("non-metal acid");
            else puts("polyatomic acid");
        }
        else puts("not an acid");
    }
    return 0;
}

T2:

这是一道贪心的题,考虑肯定每次关一扇门最劣,关两扇门最优

于是可以分别从左向右或者从右向左模拟关门,求出答案

code:

#include<bits/stdc++.h>
using namespace std;
int ans1,ans2,a[10010],n;
int main(){
    freopen("lock.in","r",stdin);
    freopen("lock.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),ans2+=a[i];
    for(int i=1;i<=n;i++){
        if(!a[i]) continue;
        ans1++;
        a[i]=0;
        a[i+1]=0;
    }
    printf("%d %d",ans1,ans2);
    return 0;
}

T3:

大模拟,暴力出奇迹。。。

code:

#include<bits/stdc++.h>
using namespace std;
int kx,ky,ans,T;
char mp[12][12];
int vis[12][12];
const int n=8,q=8,r=4,b=4,k=8;
const int qt[q][2]={1,1,1,0,1,-1,0,-1,-1,-1,-1,0,-1,1,0,1};
const int rt[r][2]={1,0,0,1,-1,0,0,-1};
const int bt[b][2]={1,1,1,-1,-1,-1,-1,1};
const int kt[k][2]={1,2,2,1,-1,2,2,-1,1,-2,-2,1,-1,-2,-2,-1};
bool check(int x,int y){return x>=1&&x<=n&&y>=1&&y<=n;}
bool solve(){
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){
        if(mp[i][j]=='k') kx=i,ky=j;
        else if(mp[i][j]=='Q'){
            for(int k=0;k<q;k++)
                for(int x=i+qt[k][0],y=j+qt[k][1];check(x,y)&&(mp[x][y]=='#'||mp[x][y]=='k');x+=qt[k][0],y+=qt[k][1])
                    vis[x][y]=1;
        }else if(mp[i][j]=='R'){
            for(int k=0;k<r;k++)
                for(int x=i+rt[k][0],y=j+rt[k][1];check(x,y)&&(mp[x][y]=='#'||mp[x][y]=='k');x+=rt[k][0],y+=rt[k][1])
                    vis[x][y]=1;
        }else if(mp[i][j]=='B'){
            for(int k=0;k<b;k++)
                for(int x=i+bt[k][0],y=j+bt[k][1];check(x,y)&&(mp[x][y]=='#'||mp[x][y]=='k');x+=bt[k][0],y+=bt[k][1])
                    vis[x][y]=1;
        }else if(mp[i][j]=='K'){
            for(int k=0;k<k;k++)
                if(check(i+kt[k][0],j+kt[k][1]))
                    vis[i+kt[k][0]][j+kt[k][1]]=1;
        }else if(i>1&&mp[i][j]=='P'){
            if(check(i-1,j-1))    vis[i-1][j-1]=1;
            if(check(i-1,j+1))    vis[i-1][j+1]=1;
        }
    }    return vis[kx][ky];
}
int main(){
    freopen("check.in","r",stdin);
    freopen("check.out","w",stdout);
    scanf("%d",&T);
    while(T--){
        ans=0;
        for(int i=0;i<=n+1;i++)
            mp[0][i]=mp[n+1][i]='#',
            mp[i][0]=mp[i][n+1]='#';
        for(int i=1;i<=8;i++)scanf("%s",mp[i]+1);
        ans=solve();
        for(int i=1;i<=8;i++){
            if(mp[1][i]=='#'&&mp[2][i]=='P'){
                mp[2][i]='#';mp[1][i]='P';
                mp[1][i]='Q',ans+=solve();
                mp[1][i]='R',ans+=solve();
                mp[1][i]='B',ans+=solve();
                mp[1][i]='K',ans+=solve(); 
                mp[1][i]='#',mp[2][i]='P';
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

T4:

考虑要让分解次数尽量多,肯定要分解成尽量大的质因子

所以可以暴力枚举出小的约数,然后除n就可以得出最大的质因子了。。。

不过貌似也可以从大向小枚举,没试过。。。

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,x;
ll ans;
ll calc(ll k){
    ll cnt=k;
    while(k>1){
        ll tot=-1;
        for(int i=2;i<=sqrt(k);i++)if(k%i==0){tot=i;break;}
        if(tot==-1){cnt++;break;}
        cnt+=k/tot;
        k/=tot;
    }
    return cnt;
}
int main(){
    freopen("sticks.in","r",stdin);
    freopen("sticks.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%lld",&x),ans+=calc(x);
    printf("%lld",ans);
    return 0;
}

T5:

首先可以得出一个n^2的DP:

#include <bits/stdc++.h>
#define mxn 100000
using namespace std;
typedef long long ll;
bool o[mxn|1];
ll rs,n,h[mxn|1],p[mxn|1],f[mxn|1];
int main() {
    freopen("race.in","r",stdin);
    freopen("race.out","w",stdout);
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",h+i);
    for(int i=2;i<=n;i++)
        scanf("%lld",p+i);
    memset(f,0x3f,sizeof(f));
    f[1]=0;
    for(int i=1;i<n;i++)
        for(int j=i+1;j<=n;j++)
            if(h[j]>h[i]) {
                f[j]=min(f[j],f[i]+h[j]-h[i]+p[j]);
                o[i]=1;    break;
            } else {
                f[j]=min(f[j],f[i]+h[i]-h[j]+p[j]);
            }
    rs=0x3f3f3f3f3f3f3f3f;
    for(int i=1;i<=n;i++)
        if(!o[i])    rs=min(rs,f[i]);
    printf("%lld\n",rs+n);
    return 0;
}

但是这样只有68分。。。

然后我们想到倒着转移,从n枚举到1

发现f[i]转移是从之前所有的f[j]中代价最小的那个转移过来的。。。

于是开一个单调栈来维护这一切。。。

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,p[100010],f[100010];
ll st[100010][2],top;
ll h[100010];
int main(){
    freopen("race.in","r",stdin);
    freopen("race.out","w",stdout);
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)scanf("%lld",&h[i]);
    for(int i=2;i<=n;i++)scanf("%lld",&p[i]);
    memset(f,127,sizeof(f));
    for(ll i=n;i>=1;i--){
        ll cnt=1e18;
        while(top&&h[st[top][0]]<=h[i]){
            cnt=min(st[top][1],cnt);
            f[i]=min(f[i],st[top][1]-i+h[i]);
            top--;
        }
        if(!top) f[i]=min(f[i],n-i+1);
        else f[i]=min(f[i],st[top][0]-i+f[st[top][0]]+h[st[top][0]]-h[i]+p[st[top][0]]);
        cnt=min(cnt,f[i]+i-h[i]+p[i]);
        st[++top][0]=i;
        st[top][1]=cnt;
    }
    printf("%lld",f[1]);
    return 0;
}
共 24 篇博客