Day1T1

思路:

非常简单的一个模拟

代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e5+100;
int a[N],n,m,cr[N];
char s[N][50];
int main() {
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;++i) {
        scanf("%d",&cr[i]);
        cin>>s[i];
    }
    int ans=0;
    for(int i=1;i<=m;++i) {
        int x,y;
        scanf("%d%d",&x,&y);
        if(x==cr[ans])
            ans=(ans-y%n+n)%n;
        else
            ans=(ans+y%n)%n;
    }
    cout<<s[ans];
    return 0;
}

Day1T3

思路:

并不是很难得一个期望dp(虽然一开始没做出来233),首先跑一边floyd找出最短路,用f[i][j][0/1]表示前i短时间,换了j次,第i段时间换(1)还是不换(0)的最小耗费。

如果不换,那么当前最有花费就是第i-1短时间换(就用前一个换的情况加上换之后的教室到当前教室的距离,当然还要考虑期望,有k[i-1]的概率会换成功,有(1-k[i-1])的概率不成功)或者不换(就直接用前一个不换的情况加上前一个教室到当前教室的距离)中更优秀的那个。即

f[i][j][0]=min(f[i-1][j][0]+a[c[i-1]][c[i]],f[i-1][j][1]+a[d[i-1]][c[i]]*k[i-1]+a[c[i-1]][c[i]]*(1-k[i-1]))

如果换,那么就比较麻烦一点,但是只要慢慢想出所有情况就可以了。

首先如果前一个不换,那么就有k[i]的概率是从c[i-1] (不换情况下的教室)到d[i] (换之后的教室)。有(1-k[i])的概率是从c[i-1]到c[i]。所以

f[i][j][1]=min(f[i][j][1],f[i-1][j-1][0]+(a[c[i-1]][d[i]])*k[i]+(a[c[i-1]][c[i]])*(1-k[i]))

如果前一个也换的换的话,那么有k[i]k[i-1]的概率是从d[i-1]到d[i],有k[i](1-k[i-1])的概率是从c[i-1]到d[i],有(1-k[i])k[i-1]的概率是从d[i-1]到c[i],有(1-k[i-1])(1-k[i-1])的概率是从c[i-1]到c[i],所以

f[i][j][1]=min(f[i][j][1], f[i-1][j-1][1]+(a[d[i-1]][d[i]])*k[i]*k[i-1]+a[d[i-1]][c[i]]*(1-k[i])*k[i-1]+a[c[i-1]][d[i]]*k[i]*(1-k[i-1])+a[c[i-1]][c[i]]*(1-k[i])*(1-k[i-1]))

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=310,M=2000+10;
const double INF=999999999;
int n,m,V,E;
int c[M],d[M];
double a[M][M];
double K[M],f[M][M][2];
inline void init() {
    cin>>n>>m>>V>>E;
    for(int i=1;i<=V;++i) for(int j=1;j<=V;++j) a[i][j]=INF;
    for(int i=1;i<=n;++i)
        cin>>c[i];
    for(int i=1;i<=n;++i) 
        cin>>d[i];
    for(int i=1;i<=n;++i) 
        cin>>K[i];
    for(int i=1;i<=E;++i) {
        int x,y;
        cin>>x>>y;
        double z;
        cin>>z;
        a[x][y]=a[y][x]=min(a[x][y],z);
    }
}
inline void floyd() {
    for(int i=1;i<=V;++i) a[i][i]=0;
    for(int k=1;k<=V;++k) 
        for(int i=1;i<=V;++i) 
            for(int j=1;j<=V;++j) 
                if(a[i][j]>a[i][k]+a[k][j])
                    a[i][j]=a[i][k]+a[k][j];
}  
int main() {
    ios::sync_with_stdio(false);
    init();
    floyd();
    if(m==0) {
        double mans=0;
        for(int i=2;i<=n;++i)
            mans+=a[c[i-1]][c[i]];
        printf("%.2f\n",mans);
        return 0;
    }
    /*for(int i=1;i<=V;++i) {
        for(int j=1;j<=V;++j) {
            printf("%.0f ",a[i][j]);
        }
        printf("\n");
    }*/
    for(int i=1;i<=n;++i) 
        for(int j=0;j<=m;++j)
            f[i][j][1]=f[i][j][0]=INF;
        f[1][1][1]=0;
        f[1][0][0]=0;
    for(int i=2;i<=n;++i) {
        for(int j=0;j<=min(m,i);++j) {
            f[i][j][0]=min(f[i-1][j][0]+a[c[i-1]][c[i]],f[i-1][j][1]+a[d[i-1]][c[i]]*K[i-1]+a[c[i-1]][c[i]]*(1-K[i-1]));
            if(j!=0)
            f[i][j][1]=min(f[i-1][j-1][0]+a[c[i-1]][d[i]]*K[i]+a[c[i-1]][c[i]]*(1-K[i]),f[i-1][j-1][1]+a[d[i-1]][d[i]]*K[i]*K[i-1]+a[d[i-1]][c[i]]*(1-K[i])*K[i-1]+a[c[i-1]][d[i]]*(1-K[i-1])*K[i]+a[c[i-1]][c[i]]*(1-K[i])*(1-K[i-1]));
        }
    }


    double ans=INF;
    for(int j=0;j<=m;++j) {
        ans=min(ans,f[n][j][0]);
        ans=min(ans,f[n][j][1]);
    }
    printf("%.2f",ans );
    return 0;
}

Day2T1

思路:

对于上面和下面的式子进行分解质因数,然后看看上面的质因数个数减去下面的质因数个数能不能达到k的质因数的要求即可。

分解质因数的时候用对于阶乘分解质因数的常用方法:比如要求1999!中能分解出多少个5,那么就把1999不断的除以5,并且把得到的数相加即可。原因显然。

但是上面方法的复杂度是nnt,明显tle,考虑优化。发现当k固定之后,对于每个n和m是固定的,并且似乎是可以转移的。所以考虑预处理。

用c[i][j]表示\(C_i^j\)是不是符合要求。用g[i][j]表示当m为j,n从j到max满足条件的数量。f[i][j]表示n为1到i,m为1到j时满足条件的数量。

然后只要考虑出f[i][j]的转移即可,显然f[i][j]=f[i][j-1]+g[i][j]

然后只要O(1)查询即可,懒得现将询问读入再预处理,所以前面的预处理全都是到2000的。

虽然跑的很慢,但是能过啊!!

Day2T2

80分思路:

一个比较明显的思路就是用优先队列模拟。每次取出队首也就是最长的那个蚯蚓,然后将它割开,重新放回队列,然后考虑其他蚯蚓增长的问题,如果挨个加上的话肯定t飞掉了,所以可以用一个变量now表示当前将每个蚯蚓都加上了st,每取出一只蚯蚓都将它的长度加上now即可,注意割开之后的蚯蚓不能加上这次增长的长度,要处理一下。然后就有80分了

100分思路:

可以发现,因为是先对最长的蚯蚓进行处理,并且割开的比例相同,所以先割开的蚯蚓割开后的占大比例的那一块肯定比后割开的要长,同理,占小比例的那块可能也比后割开的要长,这样就有了单调性,只要用三个队列,分别表示原来的蚯蚓,割开后占大比例的蚯蚓,和割开后占小比例的蚯蚓。每次操作的时候从这三个队列的队首中找出最大的那条蚯蚓进行与80分做法类似的操作即可,操作之后再分别放入第2,3个队列。(虽然是100分做法,但是一不小心就会写挂,所以还是写80分或者分段程序保险!!)

80分代码:

#include<queue>
#include<cstdio>
#include<iostream>
#define p u/v
using namespace std;
typedef long long ll;
priority_queue<ll>q;
double sta;
ll now,n,m,u,v,sum,T;
int main() {
    cin>>n>>m>>sum>>u>>v>>T;
    for(int i=1;i<=n;++i) {
        int x;
        cin>>x;
        q.push(x);
    }
    for(int i=1;i<=m;++i) {
        ll k=q.top()+now;
        q.pop();
        if(i%T==0) {
            printf("%lld ",k);
        }
        ll z=k*p;
        q.push(z-now-sum);
        q.push(k-z-now-sum);
        now+=sum;
    }
    printf("\n");
    int js=0;
    while(!q.empty()) {
        js++;
        if(js%T==0) printf("%lld ",q.top()+now);
        q.pop();
    }
    return 0;
}

100分代码:

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<queue>
#include<algorithm>
using namespace std;
#define p u/v
typedef long long ll;
queue<ll>q[4];
ll read() {
    ll x=0;ll 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;
}
bool cmp(int x,int y) {
    return x>y;
}
ll a[7100000+100],now,sum;
int main() {
    ll n=read(),m=read(),sum=read(),u=read(),v=read(),t=read();
    //cout<<p<<endl;
    q[0].push(-0x7fffffff);
    for(int i=1;i<=n;++i)
        a[i]=read();
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;++i) 
        q[1].push(a[i]);
    /*for(int i=1;i<=n;++i) 
        cout<<a[i]<<" ";
    while(!q[1].empty()) {
        cout<<q[1].front()<<" ";
        q[1].pop();
    }*/
    for(int i=1;i<=m;++i) {
        int k=0;
        for(int j=1;j<=3;++j)
            if(!q[j].empty()&&q[j].front()>q[k].front()) k=j;
        ll z=q[k].front();
        q[k].pop();
        z+=now;
        if(i%t==0) printf("%lld ",z);
        ll zz=z*p;
        //cout<<z<<" "<<zz<<endl;
        q[2].push(zz-now-sum);
        q[3].push(z-zz-now-sum);
        now+=sum;
    }
    printf("\n");
    for(int i=1;i<=n+m;++i) {
        int k=0;
        for(int j=1;j<=3;++j)
            if(!q[j].empty()&&q[j].front()>q[k].front()) k=j;
        if(i%t==0)  printf("%lld ",q[k].front()+now);
        q[k].pop();
    }
    return 0;
}

Day2T3

思路:

搜索+最优化剪枝

(不多bb是因为抄的题解啊啊啊啊)

代码:

#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
const double eps=1e-8;
bool dy(double a,double b) {
    return fabs(a-b)<eps;
}
int n,m,ans;
double x[20],y[20],pwxa[20],pwxb[20],tx[20],ty[20];
void dfs(int c,int u,int v) {
    if(u+v>=ans) return;
    if(c>n) {
        ans=u+v;
        return;
    }
    bool flag=0;
    for(int i=1;i<=u;++i) {
        if(dy(pwxa[i]*x[c]*x[c]+pwxb[i]*x[c],y[c])) {
            dfs(c+1,u,v);
            flag=1;
            break;
        }
    }
    if(!flag) {
        for(int i=1;i<=v;++i) {
            if(dy(x[c],tx[i])) continue;
            double a=(y[c]*tx[i]-ty[i]*x[c])/(x[c]*x[c]*tx[i]-tx[i]*tx[i]*x[c]);
            double b=(y[c]-x[c]*x[c]*a)/x[c];
            if(a<0) {
                pwxa[u+1]=a;
                pwxb[u+1]=b;
                double q=tx[i],w=ty[i];
                for(int j=i;j<v;++j) {
                    tx[j]=tx[j+1];
                    ty[j]=ty[j+1];
                }
                dfs(c+1,u+1,v-1);
                for(int j=v;j>i;--j) {
                    tx[j]=tx[j-1];
                    ty[j]=ty[j-1];
                }
                tx[i]=q;
                ty[i]=w;
            }
        }
        tx[v+1]=x[c];
        ty[v+1]=y[c];
        dfs(c+1,u,v+1);
    }
}
int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i) scanf("%lf%lf",&x[i],&y[i]);
        ans=100;
        dfs(1,0,0);
        printf("%d\n",ans);
    }

    return 0;
}

总结

难度就不说了,为啥没有天天爱跑步,不会。。。。但是打满暴力就可以拿一等奖。虽然天天爱跑步的恶心程度难以想象,但是部分分还是可以拿的,即便很不幸,天天爱跑步只拿10分,又很不幸期望dp写挂了只拿5分,day1还可以有100+10+5=115。又很不幸组合数只拿了质数的那40分,又又很不幸蚯蚓只会那80分的优先队列,又又又很不幸愤怒的小鸟一分也没有,day2也有40+80+0=120分了,总共就有了235分了,这刚好是山东分数线啊。而且这么不幸的话可以去跳楼了