1. 程式人生 > >noip模擬賽 #3

noip模擬賽 #3

多條 namespace num 分答 祖先 hid 技術 size ans

T1

給一個環,每個點有一個權值,把環分成三段,求最小的那段的最大值

sol:暴力

二分答案,chk就是把環搞成三倍鏈,每次枚舉起點,後面三個切割點都可以二分找

然後就Rua過去了

技術分享圖片
//yyc wenle
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 100010;
inline LL read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if
(ch == -)f = -f; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - 0; return x * f; } int n; LL s[3 * maxn],a[2 * maxn]; LL l,r; inline LL findnx(int pos,LL x){return upper_bound(s,s + 3 * n + 1,s[pos] + x - 1) - s;} inline int chk(LL x) { int pos; for(int i=0;i<n;i++) { pos
= i; pos = findnx(pos,x);if(pos > i + n)continue; pos = findnx(pos,x);if(pos > i + n)continue; pos = findnx(pos,x);if(pos > i + n)continue; return 1; } return 0; } int main() { n = read(); for(int i=1;i<=n;i++){a[i] = read();a[n + i] = a[i];a[2
* n + i] = a[i];} for(int i=1;i<=3 * n;i++)s[i] = s[i - 1] + a[i]; l = 0,r = s[n] / 3;LL ans; while(l <= r) { LL mid = (l + r) >> 1; if(chk(mid))l = mid + 1,ans = mid; else r = mid - 1; } printf("%lld\n",ans); } /* 30 1 34 44 13 30 1 9 3 7 7 20 12 2 44 6 9 44 31 17 20 33 18 48 23 19 31 24 50 43 15 */
View Code

T2

樹上選出k個點,如果選一個點,也要選他的祖先,默認選0,每個人有一個戰鬥力和一個花費

求選出的最大戰鬥力除以最大花費

sol:

分數規劃,每個點權變成了zdl - mid * hf

然後就是“樹上選出若幹點點權大於0”

然後,我們就想歪了

考慮選出的肯定是很多條鏈,樹上選出很多鏈?那豈不是...

九省聯考_林可卡特樹

然後想了半天帶權二分...咳咳

然後就去想T3了

寫完T3才發現這tm不是個樹背包嗎

然後算算復雜度

小數點後3位,最大10000,那就是1e8

log1e8 * O(n^2)顯然掛了

所以需要一個常數小的寫法

考慮dfs序,我們選一個點,可以轉移到他dfs序後一個點

不選一個點,就轉出這棵子樹

然後常數非常的優秀,不需要“證明復雜度”和size寫法

(學弟預處理size然後T了

技術分享圖片
//yyc wenle
#include<bits/stdc++.h>
#define LL long long
#define DB long double
using namespace std;
const int maxn = 100010;
const double inf = 1e9;
inline int read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == -)f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - 0;
    return x * f;
}
int n,k;
double s[maxn],p[maxn];
int first[maxn],to[maxn],nx[maxn],cnt;
int dfin[maxn],pos[maxn],dfout[maxn],dfn;
double f[2510][2510];int ccnt = 0;
inline void add(int u,int v)
{
    to[++cnt] = v;
    nx[cnt] = first[u];
    first[u] = cnt;
}
inline void dfs(int x)
{
    dfin[x] = ++dfn;
    pos[dfn] = x;
    for(int i=first[x];i;i=nx[i])dfs(to[i]);
    dfout[dfin[x]] = dfn;
}
inline int chk(double x)
{
    for(int i=1;i<=n + 2;i++)
        for(int j=0;j<=k + 1;j++)
            f[i][j] = -inf;
    f[1][0] = 0;
    for(int i=1;i<=n+1;i++)
        for(int j=0;j<=k+1;j++)
        {
            if(f[i][j] == -inf)continue;
            double cur = p[pos[i]] - x * (s[pos[i]]);
            f[dfout[i] + 1][j] = max(f[dfout[i] + 1][j],f[i][j]);
            f[dfout[i] + 1][j + 1] = max(f[dfout[i] + 1][j + 1],f[i][j] + cur);
            for(int xx=first[pos[i]];xx;xx = nx[xx])
            {
                int targ = to[xx];
                f[dfin[targ]][j + 1] = max(f[dfin[targ]][j + 1],f[i][j] + cur);
                //ccnt++;
            }
        }
    return f[n + 2][k + 1] >= 0.0;
}
const double eps = 1e-5;
int main()
{
    //freopen("rantree.in","r",stdin);
    //freopen("1.txt","r",stdin);
    //freopen("gen.out","w",stdout);
    //freopen("mactree.in","r",stdin);
    //freopen("chain.in","r",stdin);
    //freopen("juhua.in","r",stdin);
    k = read(),n = read();
    if(!k){puts("0.000");return 0;}
    for(int i=1;i<=n;i++)
    {
        scanf("%lf%lf",&s[i],&p[i]);int py = read();
        add(py,i);
    }
    dfs(0);
    //cout<<pos[1];
    double l = 0,r = 10000.0;
    for(int tms = 1;tms <= 50;tms++)
    {
        if(r - l <= eps)break;
        double mid = (l + r) / 2.0;
        if(chk(mid))l = mid + eps;
        else r = mid - eps;
    }
    //cout<<ccnt<<endl;
    printf("%.3lf",(l + r) / 2.0);
}
View Code

T3

給n個門,每個門是一個位運算和一個數,經過這個門就對這個數操作

求1~m經過這些門之後最大的數

sol:

貪心

1.如果這位選0,改了之後變成1,血賺

2.如果這位選1,改了之後變成0,血虧

3.剩下的,選0肯定比選1更小於m

技術分享圖片
//yyc wenle
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 100010;
inline int read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == -)f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - 0;
    return x * f;
}
int n,m;
char opt[50];
int num;
int main()
{
    n = read(),m = read();
    int x = (1 << 30) - 1,y = 0;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",opt);num = read();
        if(opt[0] == A)x &= num,y &= num;
        if(opt[0] == X)x ^= num,y ^= num;
        if(opt[0] == O)x |= num,y |= num;
    }
    int a = 0,b = 0;
    for(int i = (1 << 30);i;i >>= 1)
    {
        if((a | i) <= m && (x & i) > (y & i))b |= (x & i),a |= i;
        else b |= (y & i);
    }
    printf("%d\n",b);
}
View Code

AK了

noip模擬賽 #3