NOIP模拟3

A. 三元

这东西虽然过了,但是感觉它好鬼畜啊,既没有用到三进制数,也没有把所有串的单个位拿出来讨论,我的dfs只是为了生成全排列……就这玩意还写了177行……

什么鬼?

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e6 + 3;

int n, L, cnt[17][4], b[17], tot, id[4];;
bool flag;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

int qpow(int a, int b)
{
    int ans = 1;
    while(b)
    {
        if(b & 1) ans = ans * a;
        a = a * a;
        b >>= 1;
    }
    return ans;
}

void pt()
{
    for(int i=1; i<=L; i++)
    {
        printf("%d", b[i]);
    }
    printf("\n");
    tot++;
    if(tot == n) flag = 1;
}

void dfs2(int a)
{
    if(a > L)
    {
        pt(); return;
    }
    for(int i=0; i<=2; i++)
    {
        b[a] = i;
        dfs2(a+1);
        if(flag) return;
    }
}

void qqq()
{
    for(int i=1; i<=L; i++)
    {
        cnt[i][b[i]]--;
        printf("%d", b[i]);
    }
    printf("\n");
    tot++;
    if(tot == n) flag = 1;
}

void dfs4(int a)
{
    if(a > L) 
    {
        qqq(); return;
    }
    for(int i=1; i<=3; i++)
    {
        b[a] = id[i];
        dfs4(a+1);
        if(flag) return;
    }
}

void ppp()
{
    for(int i=1; i<=L; i++)
    {
        if(!cnt[i][b[i]]) return;
    }
    for(int i=1; i<=L; i++) 
    {
        cnt[i][b[i]]--;
        printf("%d", b[i]);
    }
    printf("\n");
    tot++;
    if(tot == n) flag = 1;
}

void dfs5(int a)
{
    if(a > L)
    {
        ppp(); return;
    }  
    for(int i=1; i<=3; i++)
    {
        if(!cnt[a][id[i]]) continue;
        b[a] = id[i];
        dfs5(a+1);
        if(flag) return;
    }
}

int main()
{
    freopen("three.in", "r", stdin);
    freopen("three.out", "w", stdout);
    
    n = read(); L = read();
    cnt[1][0] = cnt[1][1] = n;
    for(int i=2; i<=L; i++)
    {
        int c0 = qpow(3, L-i);
        if(c0 >= n)
        {
            cnt[i][1] = cnt[i][2] = n;
        }
        else if(c0 + c0 >= n)
        {
            cnt[i][0] = n - c0;
            cnt[i][1] = c0;
            cnt[i][2] = n;
        }
        else if(c0 + c0 + c0 >= n)
        {
            cnt[i][0] = cnt[i][1] = n - c0;
            cnt[i][2] = c0 + c0;
        }
        else 
        {
            int w = c0+c0+c0, t = n / w;
            cnt[i][0] = cnt[i][1] = cnt[i][2] = n - t * c0;
            t = n % w;
            if(c0 >= t)
            {
                cnt[i][0] -= t;
            }
            else if(c0 + c0 >= t)
            {
                cnt[i][0] -= c0; cnt[i][1] = cnt[i][1] - t + c0;
            }
            else 
            {
                cnt[i][0] -= c0; cnt[i][1] -= c0; 
                cnt[i][2] = cnt[i][2] - t + c0 + c0;
            }
        }
    }
    id[1] = 1; id[2] = 2; id[3] = 0;
    b[1] = 0; dfs4(2); 
    id[1] = 2; id[2] = 0; id[3] = 1;
    b[1] = 1; tot = 0; flag = 0; dfs5(2);
    flag = 0; tot = 0; b[1] = 2; dfs2(2);

    return 0;
}

 

D. 矩形

第二个鬼畜是h=1的特判,由于翻转过分复杂导致Cat赛时锅了,把L,R和二分的结果和填进去的数字都换成了相反的?前缀和和组合数之类的没有想到……

部分分50
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e6 + 3;
const int N = 3e5 + 3;

int d[1003][11], n, h[N], num;
ll A[maxn], L, R;

inline ll read()
{
    ll x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

void init()
{
    for(int i=1; i<=n; i++) d[i][0] = h[i];
    for(int j=1; (1<<j)<=n; j++)
    {
        for(int i=1; i+(1<<j)-1<=n; i++)
        {
            d[i][j] = min(d[i][j-1], d[i+(1<<j-1)][j-1]);
        }
    }
}

int query(int l, int r)
{
    int k = 0;
    while((1<<k+1)<=r-l+1) k++;
    return min(d[l][k], d[r-(1<<k)+1][k]);
}

inline ll get_end(ll col) {return col*(col+1)/2;}
inline bool checkL(ll mid) {return get_end(mid) < L;}
inline bool checkR(ll mid) {return get_end(mid) < R;}

int k[N], m;
ll sum;
void solve()
{
    L = read(), R = read();
    sum = 1ll * n * (n+1) / 2;//等号后面不开ll,会挂!!
    L = sum - L + 1, R = sum - R + 1;
    ll l1 = R, r1 = L;
    int l = 0, r = n;
    while(l < r)
    {
        int mid = (l + r + 1) >> 1;
        if(checkL(mid)) l = mid;
        else r = mid - 1;
    }
    int st = l + 1;
    l = 0, r = n;
    while(l < r)
    {
        int mid = (l + r + 1) >> 1;
        if(checkR(mid)) l = mid;
        else r = mid - 1;
    }
    int ed = l + 1, ss = ed;
    ll pos = l1;
    st = n - st + 1; ed = n - ed + 1;
    for(int col=ed; col>=st && pos<=r1; )
    {
        k[++m] = col;
        if(pos == get_end(ss)) col--, ss++;
        pos++;
    }
    reverse(k+1, k+1+m);
    for(int i=1; i<=m; i++) printf("%d ", k[i]);
    exit(0);
}

bool check(ll mid)
{
    ll ans = 0;
    for(int i=1; i<=n; i++)
    {
        ans += min(mid/i, (ll)n-i+1);
        if(ans >= L) return 1;
    }
    return ans >= L;
}

struct node 
{
    ll v, h, t;
    bool operator < (const node &T) const 
    {
        return v > T.v;
    }
};
priority_queue<node> q;

void solve2()
{
    L = read(); R = read();
    ll l = 1, r = sum;
    while(l < r)
    {
        ll mid = (l + r) >> 1;
        if(check(mid)) r = mid;
        else l = mid + 1;
    }
    ll ans = 0;
    for(int i=1; i<=n; i++) 
    {
        ans += min(l/i, (ll)n-i+1);
        if(l/i+1 > n-i+1) continue;
        q.push((node){1ll*i*(l/i+1), i, l/i+1});
    }
    for(ll i=L; i<=min(ans,R); i++)
    {
        printf("%lld ", l);
    }
    for(ll i=ans+1; i<=R; i++)
    {
        ll p1 = q.top().v, p2 = q.top().h, p3 = q.top().t;
        q.pop();
        printf("%lld ", p1); 
        if(p3 >= n-p2+1) continue;
        q.push((node){p1+p2, p2, p3+1});
    } 
    exit(0);
}

int main()
{
    freopen("rectangle.in", "r", stdin);
    freopen("rectangle.out", "w", stdout);
    
    n = read(); bool sp = 1, sp2 = 1;
    for(int i=1; i<=n; i++) 
    {
        h[i] = read(); sum += h[i];
        if(h[i] != 1) sp = 0;
        if(h[i] != i) sp2 = 0;
    }
    if(sp) solve();
    if(sp2) solve2();
    L = read(), R = read();
    init();
    for(int i=1; i<=n; i++)
    {
        for(int j=i; j<=n; j++)
        {
            ll wid = j - i + 1, hgt = query(i, j);
            A[++num] = wid * hgt;
        }
    }
    sort(A+1, A+1+num);
    for(int i=L; i<=R; i++)
    {
        printf("%lld ", A[i]);
    }

    return 0;
}

这个东西确实提示了不少,然后问题就变成了怎么在一般情况下求出f(s)。

用单调栈预处理每一个点作为最小值的区间,对每个点贡献高度的情况分开考虑,固定了体积的上限 s 和高度 h ,就得到了一个宽度的上限 w ,问题又变成了求在左右端点都在[l[i], r[i]]这个区间内的长度<=w并且过定点 i 的区间个数。

先考虑长度<=w的区间个数怎么求,如果w与区间长度相等,答案应该是1+2+3+...+len = (1+len)*len/2,减掉那些长度大于w的区间的答案1+2+3+...+(len-w) = (len-w)*(len-w+1)/2,上面这个式子和下面这个式子相减,先展开再提出1/2和w,就可以化简为(2*len-w+1)*w/2,这就是calc函数。

不合法的区间就是左右端点满足在[l[i], i-1]或[i,+1 r[i]]范围内长度<=w的区间个数,当然这个w要和区间长度取min。那么就也可以用calc函数求出,于是一项减两项,感觉这种理解方式比其他的容斥简单些。

既然能对 i 求出长度<=定值的个数,可以用减法得出==定值的个数,提前把答案在AL和AR范围内的都放进vector里排个序就结束了。

%%%Chen_jr

code
 #include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e6 + 3;
const int N = 3e5 + 3;

int st[maxn], top, l[maxn], r[maxn];
ll L, R, Max, lval, rval, lrnk, n, h[N];
vector<ll> ans;

inline ll read()
{
    ll x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

ll calc(ll s, ll w)
{
    if(s < 0 || s-w+1 < 0) return 0;
    return (s+s-w+1)*w/2;
}

ll check(int i, ll mx)
{
    if(mx <= 0) return 0;
    return calc(r[i]-l[i]+1, min(r[i]-l[i]+1ll, mx))-calc(r[i]-i, min(r[i]-i+0ll, mx))-calc(i-l[i], min(i-l[i]+0ll, mx));
}

ll sum(ll s)
{
    ll ans = 0;
    for(int i=1; i<=n; i++)
    {
        ans += check(i, s/h[i]);
    }
    return ans;
}

ll f(ll s)
{
    ll l = 1, r = Max;
    while(l < r)
    {
        ll mid = (l + r) >> 1;
        if(sum(mid) >= s) r = mid;
        else l = mid + 1;
    }
    return l;
}

void get_ans(ll pl, ll pr)
{
    for(int i=1; i<=n; i++)
    {
        for(ll len=(pl+h[i]-1)/h[i]; len*h[i]<=pr && len<=r[i]-l[i]+1; len++)
        {
            ll num = check(i, len) - check(i, len-1);
            for(ll j=1; j<=num; j++) ans.push_back(h[i]*len);
        }
    }
}

int main()
{
    freopen("rectangle.in", "r", stdin);
    freopen("rectangle.out", "w", stdout);
    
    n = read();
    for(int i=1; i<=n; i++) 
    {
        h[i] = read(); 
    }
    for(int i=1; i<=n+1; i++)
    {
        while(top && h[st[top]] > h[i])
        {
            r[st[top--]] = i - 1;
        }
        l[i] = st[top] + 1; st[++top] = i;
    }
    for(int i=1; i<=n; i++) Max = max(Max, 1ll*h[i]*(r[i]-l[i]+1));
    L = read(), R = read();
    lval = f(L), rval = f(R);
    lrnk = sum(lval);
    for(ll i=L; i<=min(lrnk,R); i++) ans.push_back(lval);
    get_ans(lval+1, rval-1);
    while(ans.size() < R-L+1) ans.push_back(rval);
    sort(ans.begin(), ans.end());
    for(ll x : ans) printf("%lld ", x);

    return 0;
}

 

posted @ 2022-11-15 16:37  Catherine_leah  阅读(33)  评论(0编辑  收藏  举报
/* */