noip部分题目总结

Posted wtz2333

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了noip部分题目总结相关的知识,希望对你有一定的参考价值。

6102

天天爱跑步

毒瘤题,神仙树上上差分。
先推出两个式子
(dep[u] = dep[i] + w[i]),
(dep[u] - dep[lca] = w[i] - dep[i]) 开桶差分。
因为差分会导致统计子树时会有子树外的值,所以要用前缀作差的形式来消除影响。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<set>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 7;
int n,m;
int l,pre[maxn<<1],last[maxn],other[maxn<<1];
int sum[maxn],tmp[maxn],w[maxn],ans[maxn],dep[maxn];
int f[maxn][20];
int c1[maxn<<1],c2[maxn<<1];
vector <int> a1[maxn],b1[maxn],a2[maxn],b2[maxn];
void add(int x,int y)
{
    l++;
    pre[l] = last[x];
    last[x] = l;
    other[l] = y;
}
void dfs2(int x)
{
    for(int p = last[x];p;p = pre[p])
    {
        int v = other[p];
        if(v == f[x][0])continue;
        dep[v] = dep[x] + 1;
        f[v][0] = x;
        dfs2(v);
    }
}
int lca(int u,int v)
{
    if(dep[u] < dep[v])swap(u,v);
    for(int i = 0;i <= 16;i ++)
    {
        if((dep[u] - dep[v])&(1<<i))u = f[u][i];
    }
    if(u == v)return u;
    for(int i = 17;i >= 0;i --)
    {
        if(f[u][i] != f[v][i])
        {
            u = f[u][i];
            v = f[v][i];
        }
    }
    return f[u][0];
}
void dfs(int x,int fa){
    int cnt1 = c1[dep[x] + w[x]],cnt2 = c2[w[x] - dep[x] + n];
    for(int p = last[x];p;p = pre[p]){
        int v = other[p];
        if(v == fa)continue;
        dfs(v,x);
    }
    for(int i = 0;i < a1[x].size();i ++)c1[a1[x][i]] ++;
    for(int i = 0;i < b1[x].size();i ++)c1[b1[x][i]] --;
    for(int i = 0;i < a2[x].size();i ++)c2[a2[x][i] + n] ++;
    for(int i = 0;i < b2[x].size();i ++)c2[b2[x][i] + n] --;
    ans[x] = c1[dep[x] + w[x]] - cnt1 + c2[w[x] - dep[x] + n] - cnt2;
}
int main()
{
    n = read(),m = read();
    for(int i = 1;i < n ;i ++)
    {
        int u = read(),v = read();
        add(u,v);add(v,u);
    }
    dfs2(1);
    for(int j = 1;j <= 18;j ++){
        for(int i = 1;i <= n ;i ++){
            f[i][j] = f[f[i][j-1]][j-1];
        }
    }
    for(int i = 1;i <= n ;i ++)w[i] = read();
    for(int i = 1;i <= m ;i ++){
        int u = read(),v = read();
        int x = lca(u,v);
        a1[u].push_back(dep[u]);
        b1[f[x][0]].push_back(dep[u]);
        a2[v].push_back(dep[u] - 2*dep[x]);
        b2[x].push_back(dep[u] - 2*dep[x]); 
        
    }
    dfs(1,0);
    for(int i = 1;i <= n;i ++)cout<<ans[i]<<" ";
    return 0;
}

蚯蚓

发现题目隐藏性质,减低复杂度的关键在于大根堆的log,每次切断都是单调的,便可用三个队列维护。
证明:两条蚯蚓,长度为(a,b(a > b)),(t)秒后切断(b),此时a的两条的长度(pa + tx,(1-p)x + tx),(b)两条长度(p(b + t),(1-p)(b + t))单调显而易见。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<set>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn = 7e6 + 7;
queue <ll> q1,q2,q3;
int n,m,f,u,v,t;
int a[maxn];
ll addtag;
int main()
{
    scanf("%d%d%d%d%d%d",&n,&m,&f,&u,&v,&t);
    double p = (double)u/(double)v;
    for(int i = 1;i <= n;i ++){
        scanf("%d",a + i);
    }
    sort(a + 1,a + 1 + n);
    for(int i = n;i >= 1;i --)
        q1.push(a[i]);
    for(int i = 1;i <= m;i ++){
        ll mx = -1e9,flag = 0;
        if(!q1.empty() && q1.front() > mx) mx = q1.front(),flag = 1;
        if(!q2.empty() && q2.front() > mx) mx = q2.front(),flag = 2;
        if(!q3.empty() && q3.front() > mx) mx = q3.front(),flag = 3;
        if(flag == 1) q1.pop();
        if(flag == 2) q2.pop();
        if(flag == 3) q3.pop();
        if(i % t == 0){
            printf("%lld ",mx + addtag);
        }
        mx += addtag;
        ll x = floor(p * mx);ll y = mx - x;
        x -= addtag;y -= addtag;
        q2.push(x - f);q3.push(y - f);
        addtag += f;  
    }
    cout<<endl;
    for(int i = 1;i <= n + m;i ++){
        ll mx = -1e9,flag = 0;
        if(!q1.empty() && q1.front() > mx) mx = q1.front(),flag = 1;
        if(!q2.empty() && q2.front() > mx) mx = q2.front(),flag = 2;
        if(!q3.empty() && q3.front() > mx) mx = q3.front(),flag = 3;
        if(flag == 1) q1.pop();
        if(flag == 2) q2.pop();
        if(flag == 3) q3.pop();
        if(i % t == 0){
            printf("%lld ",mx + addtag);
        }
    }
    return 0;
}

愤怒的小鸟

(dp)还是比较好想的,预处理出所有抛物线,(dp[s | line[i]] = max(dp[s] + 1))

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<set>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;
int n,m,T,ans;
double x[1010],y[1011];
int dp[(1<<18) + 1],line[10101],cnt;
bool calc(int d,double a,double b){
//  cout<<abs(a*x[d]*x[d] + b*x[d] - y[d])<<endl;
    if(fabs(a*x[d]*x[d] + b*x[d] - y[d]) <= 0.000001)return 1;
    return 0;
}
int main()
{
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i = 1;i <= n ;i ++){
            scanf("%lf%lf",&x[i],&y[i]);
        }
        memset(dp,0x3f,sizeof dp);
        dp[0] = 0;
        cnt = 0;
        for(int i = 1;i <= n ;i ++){
            line[++cnt] = (1 << (i-1));
            for(int j = i + 1;j <= n;j ++){
                if(fabs(y[i]/x[i] - y[j]/x[j]) <= 0.0001)continue;
                double a = (x[j]*y[i] - x[i]*y[j])/((x[i]-x[j])*x[i]*x[j]);
                double b = (y[i] - a*x[i]*x[i])/x[i];
                if(a > 0)continue;
                int s = 0;
                for(int k = 1;k <= n ;k ++){
                    if(calc(k,a,b)){
                        s |= (1 << (k-1));
                    }
                }
                line[++cnt] = s;
            }
        }
        for(int s = 0;s < (1 << n);s ++){
            for(int i = 1;i <= cnt;i ++){
                dp[s | line[i]] = min(dp[s] + 1,dp[s|line[i]]);
            }
        }
        printf("%d
",dp[(1 << n)-1]);
    }
    return 0;
}

换教室
一个期望(DP)入门题,(dp[i][j][0/1])表示前(i)个改了(j)门,第(i)门改/没改的期望,转移比较长要仔细写。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<set>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;
int read(){
    int x;scanf("%d",&x);return x;
}
const int maxn = 2001;
int n,m,v,e,c[maxn],d[maxn];
double dis[maxn][maxn];
double dp[maxn][maxn][2],k[maxn];
int main()
{
    n = read(),m = read(),v = read(),e = read();
    for(int i = 1;i <= n;i ++)c[i] = read();
    for(int i = 1;i <= n;i ++)d[i] = read();
    for(int i = 1;i <= n;i ++)scanf("%lf",&k[i]);
    for(int i = 1;i <= v;i ++){
        for(int j = 1;j <= v;j ++){
            dis[i][j] = 1e9 + 7;
        }
    }
    for(int i = 1;i <= e;i ++){
        int a = read(),b = read();double w;
        scanf("%lf",&w);
        dis[a][b] = dis[b][a] = min(dis[a][b],w);
    }
    for(int i = 1;i <= v;i ++)dis[i][i] = 0;
    for(int p = 1;p <= v;p ++){
        for(int i = 1;i <= v;i ++){
            for(int j = 1;j <= v;j ++)
            dis[i][j] = min(dis[i][j],dis[i][p]+dis[p][j]);
        }
    }
    for(int i = 0;i <= n ;i ++){
        for(int j = 0 ;j <= m ;j ++){
            dp[i][j][0] = dp[i][j][1] = 1e9 + 7;
        }
    }
    dp[1][0][0] = dp[1][1][1] = 0;
    double ans = 1e9 + 7;
    for(int i = 2;i <= n ;i ++){
        for(int j = 0;j <= min(m,i) ;j ++){
            double p1 = k[i],p2 = 1-k[i],p3 = k[i-1],p4 = 1 - k[i-1];
            dp[i][j][0] = min(dp[i-1][j][0] + dis[c[i-1]][c[i]],dp[i-1][j][1] + dis[d[i-1]][c[i]]*p3 + dis[c[i-1]][c[i]]*p4);
            if(j)
            dp[i][j][1] = min(dp[i-1][j-1][0] + dis[c[i-1]][c[i]]*p2 + dis[c[i-1]][d[i]]*p1,dp[i-1][j-1][1] + dis[c[i-1]][c[i]]*p2*p4 + dis[c[i-1]][d[i]]*p4*p1 + dis[d[i-1]][c[i]]*p3*p2 + dis[d[i-1]][d[i]]*p3*p1);
        //  printf("%.2lf %.2lf %d %d
",dp[i][j][1],dp[i][j][0],i,j);
        }
    }
    for(int i = 0;i <= m ;i ++){
        ans = min(min(dp[n][i][0],dp[n][i][1]),ans);
    }
    printf("%.2lf",ans);
    return 0;
}

以上是关于noip部分题目总结的主要内容,如果未能解决你的问题,请参考以下文章

NOIp 2016 总结

总结NOIP2017 总结

2016.10.29 清北学堂NOIP冲刺班Day1 AM 考试总结

备战NOIP[算法总结] 二分查找

刷题总结——子串(NOIP2015)

[OI]Noip 2018(普及组)总结