Contest 传送门
考虑直接贪心,每次把最优的两个顺着往后放,需要保证选出来的两个合法
也就是第一个数在它所在的段的奇数位,第二个数与第一个数在不同位
选出来过后有会多两个断点分成 3 段
然后就是在所有合法位置中选一个最小的再删除
我的沙雕做法:
考虑到增加断点过后会有奇数段变成偶数段,于是乎建两棵 ,互换子树维护集合最小值即可
其实正解就是用个对维护每个段,ST 表维护奇数偶数的最小值
报错管都没管挂成 10 分。。。
#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
#define mp make_pair
cs int N = 2e5 + 50;
cs int INF = 1e9;
int n, a[N], ps[N], lg[N];
multiset<int> B;
typedef multiset<int>::iterator It;
struct ST{
int a[N][20];
ST(){ memset(a, 0x3f, sizeof(a)); }
void build(){
for(int j = 1; (1 << j) <= n; ++j)
for(int i = 1; i + (1<<j) - 1 <= n; ++i)
a[i][j] = min(a[i][j-1], a[i+(1<<j-1)][j-1]);
}
int query(int l, int r){
if(l > r) return INF; int x = lg[r-l+1];
return min(a[l][x],a[r-(1<<x)+1][x]);
}
}M[2];
namespace Splay{
cs int N = ::N<<1;
bool FLAG; int rt[2],nd;
int ch[N][2],fa[N],vl[N],mi[N],sz[N];
void init(){ mi[0] = vl[0] = INF; }
void pushup(int x){
sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1;
mi[x] = min(vl[x], min(mi[ch[x][0]],mi[ch[x][1]]));
}
int build(int anc, int l, int r){
if(l > r) return 0;
int mid = (l+r) >> 1, x = ++nd; fa[x] = anc;
if(mid & 1 ^ FLAG) vl[x] = a[mid];
else vl[x] = INF;
ch[x][0] = build(x, l, mid-1);
ch[x][1] = build(x, mid+1, r);
pushup(x); return x;
}
int get(int x){ return ch[fa[x]][1] == x; }
void rot(int x){
int y = fa[x], z = fa[y], k = get(x);
ch[z][get(y)] = x; fa[x] = z;
ch[y][k] = ch[x][k^1]; fa[ch[x][k^1]] = y;
ch[x][k^1] = y; fa[y] = x; pushup(y); pushup(x);
}
void splay(int x, int gl, int &rt){
while(fa[x] ^ gl){
int y = fa[x];
if(fa[y] ^ gl) rot(get(x) ^ get(y) ? x : y); rot(x);
} if(gl == 0) rt = x;
}
int find(int x, int k){
if(sz[ch[x][0]] >= k) return find(ch[x][0], k);
else if(sz[ch[x][0]] + 1 == k) return x;
return find(ch[x][1], k - sz[ch[x][0]] - 1);
}
void ban(int k){
int x = find(rt[0], k + 1); splay(x, 0, rt[0]);
vl[x] = INF; pushup(x);
}
void Swap(int l, int r){
if(l > r) return;
int x1 = find(rt[0], l), y1 = find(rt[0], r + 2);
splay(x1, 0, rt[0]); splay(y1, rt[0], rt[1]);
int x2 = find(rt[1], l), y2 = find(rt[1], r + 2);
splay(x2, 0, rt[1]); splay(y2, rt[1], rt[1]);
int p1 = ch[y1][0], p2 = ch[y2][0];
ch[y1][0] = p2; fa[p2] = y1; pushup(y1); pushup(x1);
ch[y2][0] = p1; fa[p1] = y2; pushup(y2); pushup(x2);
}
}
int main(){
n = read();
bool Subtask = true;
for(int i = 1; i <= n; i++){
a[i] = read(); ps[a[i]] = i;
M[i & 1].a[i][0] = a[i];
if(a[i] ^ i) Subtask = false;
}
if(Subtask){
for(int i = 1; i <= n; i++) cout << a[i] << " ";
return 0;
}
B.insert(0); B.insert(n + 1);
for(int i = 2; i <= n; i++) lg[i] = lg[i>>1] + 1;
M[0].build();
M[1].build();
Splay::init(); a[0] = a[n+1] = INF;
Splay::FLAG = 0; Splay::rt[0] = Splay::build(0,0,n+1);
Splay::FLAG = 1; Splay::rt[1] = Splay::build(0,0,n+1);
for(int T = 1, up = n/2; T <= up; T++){
int pl = ps[Splay::mi[Splay::rt[0]]];
It it = B.lower_bound(pl); int nxt = *it;
int pr = ps[M[pl & 1 ^ 1].query(pl + 1, nxt - 1)];
cout << a[pl] << " " << a[pr] << " ";
Splay::ban(pl); Splay::ban(pr);
B.insert(pl); B.insert(pr);
Splay::Swap(pl + 1, pr - 1);
} return 0;
}
#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
#define mp make_pair
cs int N = 2e5 + 50;
cs int INF = 1e9;
int n, a[N], ps[N], lg[N];
struct ST{
int a[N][20];
ST(){ memset(a, 0x3f, sizeof(a)); }
void build(){
for(int j = 1; (1 << j) <= n; ++j)
for(int i = 1; i + (1<<j) - 1 <= n; ++i)
a[i][j] = min(a[i][j-1], a[i+(1<<j-1)][j-1]);
}
int query(int l, int r){
if(l > r) return INF; int x = lg[r-l+1];
return min(a[l][x],a[r-(1<<x)+1][x]);
}
}M[2];
struct node{
int l, r, v;
bool operator < (cs node &a) cs{ return v > a.v; }
};
priority_queue<node> S;
int qry(int l, int r){ return M[l&1].query(l, r); }
int main(){
n = read();
for(int i = 1; i <= n; i++){
a[i] = read(); ps[a[i]] = i;
M[i & 1].a[i][0] = a[i];
}
for(int i = 2; i <= n; i++) lg[i] = lg[i>>1] + 1;
M[0].build();
M[1].build();
S.push((node){1, n, qry(1,n)});
for(int T = 1, up = n/2; T <= up; T++){
node now = S.top(); S.pop();
int pl = ps[now.v];
int pr = ps[M[pl & 1 ^ 1].query(pl + 1, now.r)];
cout << a[pl] << " " << a[pr] << " ";
if(pl-1 >= now.l) S.push((node){now.l,pl-1,qry(now.l,pl-1)});
if(pl+1 <= pr-1) S.push((node){pl+1,pr-1,qry(pl+1,pr-1)});
if(pr+1 <= now.r) S.push((node){pr+1,now.r,qry(pr+1,now.r)});
} return 0;
}
不是很难的数位 只不过考场没有仔细想
预处理前 i 位数的个数,数拼起来的长度,某个数出现的次数
然后单点查的时候可以先得到长度然后得到是什么数
查前缀和可以先把 < 当前位数的加上,然后统计当前位数的
统计当前位数的一个比较好的方法是看每一位出现了多少次
#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
cs int N = 20;
typedef long long ll;
cs ll INF = 1e18;
int T; ll len[N], deg[N], sm[16][N];
char out[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
void prework(){
deg[0] = 1;
for(int i = 1; deg[i-1] < INF; i++){
deg[i] = deg[i - 1] * 15;
len[i] = deg[i] * i + len[i - 1];
sm[0][i] = deg[i] / 16 * (i - 1);
for(int c = 1; c < 16; c++)
sm[c][i] = deg[i] / 15 + deg[i] / 16 * (i - 1);
deg[i] += deg[i - 1];
}
for(int i = 0; deg[i-1] < INF; i++) --deg[i];
}
int query(ll k){
int m = 0;
for(m = 0; len[m] <= k; m++);
k -= len[m - 1];
ll x = deg[m - 1] + k / m;
k -= k / m * m;
if(!k) return x & 15;
++x; return (x >> (m - k << 2)) & 15;
}
ll ask(int d, ll x, int c){
ll ans = 0;
for(int i = 1; i < d; i++) ans += sm[c][i];
ll v = x - deg[d - 1], res = 0;
for(int i = 1; i < d; i++){
ans += (ll)(v / 16 + (v % 16 > c)) << (i - 1 << 2);
if(v % 16 == c) ans += res;
res += (ll)(v % 16) << (i - 1 << 2);
v /= 16;
}
if(c){
ans += (ll)(v % 16 >= c) << (d - 1 << 2);
if(v % 16 == c - 1) ans += res;
} return ans;
}
ll qrysum(ll k, int c){
int m = 0;
for(m = 0; len[m] <= k; m++);
k -= len[m - 1];
ll x = deg[m - 1] + k / m;
k -= k / m * m;
ll ans = ask(m, x, c);
for(ll i = 1; i <= k; i++){
int t = ((x + 1) >> (m - i << 2)) & 15;
if(t == c) ++ans;
} return ans;
}
int main(){
prework();
T = read(); char ch[3];
while(T--){
int op = read(); ll x; scanf("%lld", &x);
if(op == 1) cout << out[query(x)] << '\n';
else{
scanf("%s", ch); int c = 0;
if(isdigit(ch[0])) c = ch[0] - '0';
else c = ch[0] - 'A' + 10;
cout << qrysum(x,c) << '\n';
}
} return 0;
}
挺有意思的数数题
考虑 ,令 为到第 列,有 行 (这 行都若干个黑色)
我们新增一列,这一列要么是自己列的本质不同列,要么是新增行的本质不同行
否则就是重复的
枚举新增不同行转移,如果没有新增,那么一定是本质不同列,一列可以有 个
如果有新增本质不同行,假设有 个,把 个放进去,在这 个的最上方或是最下方还可能有黑格因为对本质不同列有影响
假设最上方的格子行是 ,最下方是 ,那么这种方案要有 的贡献
发现就是添加两个特殊行进去排列(喜闻乐见的套路),方案数
优化转移,
#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
cs int Mod = 998244353;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int ksm(int a, int b){ int ans = 1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans, a); return ans; }
void Add(int &a, int b){ a = add(a, b); }
void Mul(int &a, int b){ a = mul(a, b); }
cs int N = 8050;
#define poly vector<int>
int n, m, fac[N], ifac[N];
int Binom(int n, int m){ return mul(fac[n],mul(ifac[n-m],ifac[m])); }
void prework(int n){
fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
for(int i = 2; i <= n; i++) fac[i] = mul(fac[i-1], i);
ifac[n] = ksm(fac[n], Mod-2);
for(int i = n-1; i >= 2; i--) ifac[i] = mul(ifac[i+1], i+1);
}
cs int C = 14;
poly w[C+1];
void NTT_init(){
for(int i = 1; i <= C; i++) w[i].resize(1<<i-1);
int wn = ksm(3,(Mod-1)/(1<<C)); w[C][0] = 1;
for(int i = 1; i < (1<<C-1); i++) w[C][i] = mul(w[C][i-1],wn);
for(int i = C-1; i; i--) for(int j = 0; j < (1<<i-1); j++) w[i][j]=w[i+1][j<<1];
}
int bit, up; poly rev;
void init(int deg){
bit = 0, up = 1; while(up < deg) up <<= 1, ++bit; rev.resize(up);
for(int i = 0; i < up; i++) rev[i] = (rev[i>>1]>>1)|((i&1)<<bit-1);
}
void NTT(poly &a, int typ){
for(int i = 0; i < up; i++) if(i < rev[i]) swap(a[i], a[rev[i]]);
for(int i = 1, l = 1; i < up; i <<= 1, ++l)
for(int j = 0; j < up; j += (i<<1))
for(int k = 0; k < i; k++){
int x = a[k+j], y = mul(w[l][k],a[k+j+i]);
a[k+j] = add(x,y); a[k+j+i] = dec(x,y);
}
if(typ == -1){
reverse(a.begin()+1,a.end());
for(int i=0,iv=ksm(up,Mod-2); i<up; i++) Mul(a[i],iv);
}
}
int main(){
n = read(), m = read(); prework(n + 5);
NTT_init(); init(n + n + 2);
poly f; f.push_back(1);
poly g(n+1, 0);
for(int i = 1; i <= n; i++) g[i] = ifac[i + 2];
g.resize(up); NTT(g, 1);
for(int j = 1; j <= m; j++){
poly h = f; h.resize(up);
for(int i = 0; i <= n; i++) Mul(h[i],ifac[i]);
NTT(h, 1);
for(int i = 0; i < up; i++) Mul(h[i],g[i]);
NTT(h, -1); h.resize(n+1); f.resize(n+1);
for(int i = 0; i <= n; i++)
f[i] = add(mul(h[i],fac[i+2]), mul(i*(i+1)/2+1,f[i]));
}
int ans = 0;
for(int i = 0; i <= n; i++)
Add(ans, mul(f[i], Binom(n,i)));
cout << ans; return 0;
}