Day1t1

思路

就是模拟,只是怎样更优雅的模拟而已,不过多点if也没关系,能拿分才是关键嘛。

代码:

#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
int read() {
    int x=0,f=1;
    char c=getchar();
    while(!isdigit(c)) {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(isdigit(c)) {
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}
const int N=210;
int pd[10][10];
void pre() {
    pd[0][2]=pd[0][3]=pd[1][3]=pd[2][4]=pd[3][4]=1;
    for(int i=0;i<=4;++i)
        for(int j=0;j<i;++j)
            pd[i][j]=pd[j][i]^1;
}
int a[N],b[N];
int main() {
    pre();
    int na,nb,n;
    scanf("%d%d%d",&n,&na,&nb);
    for(int i=0;i<na;++i)
        scanf("%d",&a[i]);
    for(int i=0;i<nb;++i) 
        scanf("%d",&b[i]);
    int ansa=0,ansb=0,ajs=0,bjs=0;
    for(int i=1;i<=n;++i) {
        ajs%=na;
        bjs%=nb;
        ansa+=pd[a[ajs]][b[bjs]];
        ansb+=pd[b[bjs]][a[ajs]];
        ajs++;
        bjs++;
    }
    printf("%d %d\n",ansa,ansb);
    return 0;
}

Day1t2

思路

仔细理解题意可以发现,其实题目就是让着对于每个点,将其所能到达的每个点两两相乘,求出所得之和和最大值。如果每个点都n方枚举的话应该是过不了的。所以可以稍微推一下O(n)计算的方法,其实就是一边对前面的孩子求前缀和,一边乘当前的孩子,最后再乘2即可。总复杂度似乎是\(O(n^2)\)的,但是发现因为这是一棵树,所以对于每条边只会计算到两次,实际复杂度是\(O(n)\)的。

最大值不取模,最大值不取模,最大值不取模

代码:

#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const int N=200000+1000,mod=10007;
ll read() {
    ll x=0,f=1;
    char c=getchar();
    while(!isdigit(c)) {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(isdigit(c)) {
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}
struct node
{
    int v,nxt;
}e[N*2];
int ejs;
int head[N];
int w[N];
int ans,sum;
void add(int u,int v) {
    e[++ejs].v=v;e[ejs].nxt=head[u];head[u]=ejs;
}
int K=0;
void work(int x) {
    int kk=0;
    int mx=-1,tmx=-1;
    for(int i=head[x];i;i=e[i].nxt)
        kk+=w[e[i].v];
    for(int i=head[x];i;i=e[i].nxt) {
        K++;
        int v=e[i].v;
        sum+=(kk-w[v])*w[v]%mod;
        sum%=mod;
        if(w[v]>mx) {
            tmx=mx;
            mx=w[v];
        }
        else if(w[v]>tmx) tmx=w[v];
    }
    ans=max(ans,mx*tmx);
}
int main() {
    int n=read();
    for(int i=1;i<n;++i) {
        int x=read(),y=read();
        add(x,y);
        add(y,x);
    }
    for(int i=1;i<=n;++i) w[i]=read();
    for(int i=1;i<=n;++i) work(i);
    printf("%d %d\n",ans,sum);
    return 0;
}

Day1t3

75分思路

用f[i][j]表示到位置i,高度为j所需要花费的最小点击次数。然后每次利用当前可以存在的位置去更新下一个位置即可。复杂度\(O(nmk)\)

100分思路

在上面思路的基础上可以发现,第三层枚举,也就是枚举下一层可能存在的位置的时候,可以不必从当前位置转移,而是从下一位置已经转移了的位置进行转移。比如现在位置是1,每点击一下会上生2,那么下一位置位于5的最小点击次数可以不用f[i][1]+2更新,而是用f[i+1][3]+1,明显f[i+1][3]之前已经被更新过了。需要注意的是,小鸟再升到最高处之后,可以继续点击,只不过不会再升了而已,需要特殊处理一下。

代码

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=10100,INF=0x3f3f3f3f;
int f[N][3010],X[N],Y[N],a[N],L[N],H[N];
int n,m,K;
int js,bzz[N];
int read() {
    int x=0,f=1;
    char c=getchar();
    while(!isdigit(c)) {
        if(c=='-') f=-1;
        c=getchar();
    }
    while(isdigit(c)) {
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}
int main() {
    n=read(),m=read(),K=read();
    memset(f,0x3f,sizeof(f));
    for(int i=0;i<n;++i) {
        H[i]=m+1;
        X[i]=read();
        Y[i]=read();
    }
    H[n]=m+1;
    for(int i=1;i<=K;++i) {
        int x=read();
        bzz[x]=1;
        L[x]=read();
        H[x]=read();
    }
    for(int i=1;i<=m;++i)
        f[0][i]=0;
    for(int i=1;i<=n;++i) {
        for(int j=X[i-1]+1;j<=m+X[i-1];++j)
            f[i][j]=min(f[i][j-X[i-1]]+1,f[i-1][j-X[i-1]]+1);
        for(int j=m+1;j<=m+X[i-1];++j)
            f[i][m]=min(f[i][m],f[i][j]);
        for(int j=1;j<=m-Y[i-1];++j) 
            f[i][j]=min(f[i][j],f[i-1][j+Y[i-1]]);
        for(int j=1;j<=L[i];++j)
            f[i][j]=INF;
        for(int j=H[i];j<=m;++j)
            f[i][j]=INF;
        int bz=0;
        for(int j=L[i];j<H[i];++j) {
            if(f[i][j]<INF) {
                bz=1;
                break;
            }
        }
        if(bz==0) {
            printf("0\n%d\n",js);
            return 0;
        }
        if(bzz[i]) js++;
    }
    int ans=INF;
    for(int i=L[n]+1;i<H[n];++i) ans=min(ans,f[n][i]);
    printf("1\n%d\n",ans);
    return 0;
}

Day2t1

思路

考虑到数据范围很小,可以枚举每个路口,然后在扫一遍。去看如果将网络发射器安在当前位置会有多少收益,取最大值即可。复杂度\(O(n^4)\)

代码

#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
ll a[200][200];
int d;
int n;
ll work(int x,int y) {
    ll ans=0;
    for(int i=max(x-d,0);i<=min(x+d,128);++i)
        for(int j=max(y-d,0);j<=min(y+d,128);++j)
            ans+=a[i][j];
    return ans;
}
ll read() {
    ll x=0;char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) {x=x*10+c-'0';c=getchar();}
    return x;
}
ll ans,pans;
int main() {
    d=read();n=read();
    for(int i=1;i<=n;++i) {
        int x=read(),y=read(),z=read();
        a[x][y]=z;
    }
    for(int x=0;x<=128;++x) {
        for(int y=0;y<=128;++y) {
            ll z=work(x,y);
            if(z>ans) pans=1,ans=z;
            else if(z==ans) pans++;
        }
    }
    printf("%lld %lld\n",pans,ans);
    return 0;
}

Day2t2

思路

建反边,先在反边跑一边bfs找出可以走的边,然后在正着bfs即可。记得以前发过一篇比较详细的博客

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N=300000;
int S,T,n,m,head1[N],head2[N],js1,js2;
int vis[5][N];
struct node {
    int v,nxt;
}e1[N],e2[N];
void add1(int u,int v) {
    e1[++js1].v=v;e1[js1].nxt=head1[u];head1[u]=js1;
}
void add2(int u,int v) {
    e2[++js2].v=v;e2[js2].nxt=head2[u];head2[u]=js2;
}
queue<int>q;
int dis[N];
void bfs1() {
    vis[1][T]=1;
    while(!q.empty()) q.pop();
    q.push(T);
    while(!q.empty()) {
        int u=q.front();
        q.pop();
        for(int i=head2[u];i;i=e2[i].nxt) {
            int v=e2[i].v;
            if(!vis[1][v]) q.push(v),vis[1][v]=1;
        }
    }
}
void bfs2() {
    dis[S]=0;
    while(!q.empty()) q.pop();
    if(vis[2][S]==0)
    q.push(S);
    while(!q.empty()) {
        int u=q.front();
        q.pop();
        for(int i=head1[u];i;i=e1[i].nxt) {
            int v=e1[i].v;
            if(vis[2][v]==1) continue;
            if(dis[v]==-1) dis[v]=dis[u]+1,q.push(v);
            if(v==T) return;
        }
    }
}
int main() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i) {
        int x,y;
        scanf("%d%d",&x,&y);
        if(x==y) continue;
        add1(x,y);
        add2(y,x);
    }
    scanf("%d%d",&S,&T);
    bfs1();
    for(int i=1;i<=n;++i) {
        for(int j=head1[i];j;j=e1[j].nxt) {
            if(vis[1][e1[j].v]==0) {
                vis[2][i]=1;
                break;
            }
        }
    }
    memset(dis,-1,sizeof(dis));
    bfs2();
    cout<<dis[T];
    return 0;
}

Day2t3

思路

这数据给的真的很微妙。数组a这么大,也是很吓人啊。但是仔细一想就可以知道,如果找到的x满足给出a[i]数组的话,那么即使对a[i]数组取模,依然可以满足方程。所以就可以写个读优,然后将a[i]取模即可。然后就是枚举x带入了。如果直接用快速幂计算的话,复杂度达到了\(O(nmlog(m))\)所以就可以依次将每个x提出来(详见秦九韶算法),就可以O(n)计算了。

代码

#include<iostream>
#include<cstdio>
using namespace std;
const int N=110,mod=1e9+7;
typedef long long ll;
ll a[N];
int n,m;
ll read() {
    ll x=0,f=1; char c=getchar();
    while(!isdigit(c)) {
        if(c=='-') f=-1;
        c=getchar();
    } 
    while(isdigit(c)) {
        x=x*10+c-'0';
        x%=mod;
        c=getchar();
    }
    return x*f;
}
ll work(int now,int x) {
    ll ans=0;
    for(int i=now;i>=1;--i) {
        ans=(ans+a[i])*x%mod;
    }
    return ans+a[0]%mod;
}
int ans[2000000];
int main() {
    n=read();m=read();
    for(int i=0;i<=n;++i)
        a[i]=read();
    int js=0;
    for(int i=1;i<=m;++i)
        if(work(n,i)==0) ans[++js]=i;
    printf("%d\n",js);
    for(int i=1;i<=js;++i) {
        printf("%d\n",ans[i]);
    }
    return 0;
}

总结

这年的题目是比较简单的,但是题面里面全是细节,一不注意就100分变0分了。