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;
}
时光花火,水月星辰