• [noip][2016]


    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
    ",mans);
            return 0;
        }
        /*for(int i=1;i<=V;++i) {
            for(int j=1;j<=V;++j) {
                printf("%.0f ",a[i][j]);
            }
            printf("
    ");
        }*/
        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("
    ");
        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("
    ");
        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
    ",ans);
        }
    
        return 0;
    }
    

    总结

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

  • 相关阅读:
    [NOIP2013提高组] CODEVS 3287 火车运输(MST+LCA)
    BZOJ 2763: [JLOI2011]飞行路线(最短路)
    BZOJ 1083: [SCOI2005]繁忙的都市(MST)
    USACO Seciton 5.4 Canada Tour(dp)
    HDOJ 3415 Max Sum of Max-K-sub-sequence(单调队列)
    POJ2823 Sliding Window(单调队列)
    USACO Section 5.4 TeleCowmunication(最小割)
    [IOI1996] USACO Section 5.3 Network of Schools(强连通分量)
    USACO Section 5.3 Milk Measuring (IDDFS+dp)
    USACO Section 5.3 Big Barn(dp)
  • 原文地址:https://www.cnblogs.com/wxyww/p/9609758.html
Copyright © 2020-2023  润新知