NOI 模拟赛
管老师的题!
T1
给一个序列,多次询问一个区间去重排序后满足每一项是前一项 +1 ,长度为 1,2,...10 的极长子区间个数
$n \leq 10^6$
sol:
正解不懂,考场上莫队打挂,考后发现莫队就过了...
每个数插进去之后在他值域的前面 $10$ 个后面 $10$ 个找一下即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize("Ofast,no-stack-protector") #pragma GCC optimize("O3") #pragma GCC target("avx") #include <bits/stdc++.h> #define LL long long #define rep(i, s, t) for(register int i = (s), i##end = (t); i <= i##end; ++i) #define dwn(i, s, t) for(register int i = (s), i##end = (t); i >= i##end; --i) using namespace std; namespace IO{ const int BS=(1<<23)+5; int Top=0; char Buffer[BS],OT[BS],*OS=OT,*HD,*TL,SS[20]; const char *fin=OT+BS-1; char Getchar(){if(HD==TL){TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);} return (HD==TL)?EOF:*HD++;} void flush(){fwrite(OT,1,OS-OT,stdout);} void Putchar(char c){*OS++ =c;if(OS==fin)flush(),OS=OT;} void write(int x){ if(!x){Putchar('0');return;} if(x<0) x=-x,Putchar('-'); while(x) SS[++Top]=x%10,x/=10; while(Top) Putchar(SS[Top]+'0'),--Top; } int read(){ int nm=0,fh=1; char cw=Getchar(); for(;!isdigit(cw);cw=Getchar()) if(cw=='-') fh=-fh; for(;isdigit(cw);cw=Getchar()) nm=nm*10+(cw-'0'); return nm*fh; } } using namespace IO; const int maxn = 2e6 + 10; int n, q, mx, a[maxn], bl[maxn], cnt[maxn], ans[13], fans[maxn][13]; struct Ques { int l, r, id; } qs[maxn]; inline void inc(int x) { cnt[x]++; if(cnt[x] > 1) return; int pnt = 0, lpos = x, rpos = x; rep(i, x + 1, min(mx, x + 11)) { if(!cnt[i]) break; pnt++; rpos = i; } ans[pnt]--; pnt = 0; dwn(i, x - 1, max(1, x - 11)) { if(!cnt[i]) break; pnt++; lpos = i; } ans[pnt]--; if(rpos - lpos + 1 <= 10) ans[rpos - lpos + 1]++; } inline void dec(int x) { cnt[x]--; if(cnt[x] > 0) return; int pnt = 0; int lpos = x, rpos = x; dwn(i, x - 1, max(1, x - 11)) { if(!cnt[i]) break; pnt++; lpos = i; } ans[pnt]++; pnt = 0; rep(i, x + 1, min(mx, x + 11)) { if(!cnt[i]) break; pnt++; rpos = i; } ans[pnt]++; if(rpos - lpos + 1 <= 10) ans[rpos - lpos + 1]--; } int main() { //freopen("t1.in","r",stdin); //freopen("t1.owt","w",stdout); n = read(), q = read(); int SZ = sqrt(n); rep(i, 1, n) mx = max(mx, (a[i] = read())), bl[i] = (i - 1) / SZ + 1; rep(i, 1, q) qs[i].l = read(), qs[i].r = read(), qs[i].id = i; sort(qs + 1, qs + q + 1, [](const Ques &a, const Ques &b) { return (bl[a.l] == bl[b.l]) ? ((bl[a.l] & 1) ? (a.r < b.r) : (a.r > b.r)) : a.l < b.l; }); int l = qs[1].l, r = qs[1].l - 1; rep(i, 1, q) { while(l > qs[i].l) l--, inc(a[l]); while(r < qs[i].r) r++, inc(a[r]); while(l < qs[i].l) dec(a[l]), l++; while(r > qs[i].r) dec(a[r]), r--; memcpy(fans[qs[i].id], ans, sizeof(ans)); } rep(i, 1, q) { rep(j, 1, 10) Putchar('0' + fans[i][j] % 10); Putchar('\n'); } flush(); } /* 10 5 1 1 3 3 5 5 7 7 9 15 1 10 2 4 3 5 5 5 10 10 5000000000 2000000000 2000000000 1000000000 1000000000 */
T2
给一个数 $n$,$n$ 以内随机一个数 $x$,然后给定一个概率 $p$,有 $p$ 的概率选一个 $1 \sim n-1$ 以内与 $x$ 异或值最大的数与 $x$ 异或,$1-p$ 的概率选一个 $1 \sim n-1$ 的随机数与 $x$ 异或,求异或值的期望
$n \leq 10^{18}$
sol:
发现两部分可以分开统计,都随机那个按位搞一下就行了,最大的那个可以考虑 $n-1$ 这个数每一位的贡献
显然,如果 $n-1$ 某一位是 $1$,则比这一位小的都可以是 $1$,如果是 $0$ 就扔下去继续算就可以了,可以逐位统计这一位有多少个 $1$ 来算贡献
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> #define LL long long #define DB long double #define rep(i, s, t) for(register int i = (s), i##end = (t); i <= i##end; ++i) #define dwn(i, s, t) for(register int i = (s), i##end = (t); i >= i##end; --i) using namespace std; 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; } LL n; double p; DB ans; int dig[70], top; int main() { cin >> n >> p; LL cur = n - 1; while(cur) { dig[++top] = cur % 2; cur = cur >> 1; } dwn(i, top, 1) { LL f = cur; if(dig[i]) { f += (((n - 1) & ((1LL << (i - 1))) - 1) + 1), cur += (1LL << (i - 2)); } ans += 1.0 * f * (n - f) * 2 * (1LL << (i - 1)); } ans = 1.0 * ans / n; ans = 1.0 * ans / n; DB re = 1.0 * n * ((1LL << (top)) - 1); cur = (1LL << (top)); dwn(i, top, 1) { if(dig[i]) cur >>= 1; else re -= 1.0 * (cur >> 1) * (1LL << (i - 1)); } re = 1.0 * re / n; ans = ans * (1 - p) + re * p; double output = ans; cout << fixed << showpoint << setprecision(6) << output << endl; }
T3
有 $n$ 个编号为 $[L,R]$ 的人,你每次标记一个人就要标记所有编号为他的倍数的人,有标记的可以再选一遍,但主动选过一遍的就不能再选,如果所有人都被标记,游戏结束,求所有方案的主动选次数和,膜 $10^9+7$
$L,R \leq 10^7$
sol:
按整除关系建一张图,如果 $A$ 能整除 $B$ 就连 $A \rightarrow B$,游戏结束当且仅当所有入度为 $0$ 的点都被选
可以筛出所有入度为 $0$ 的点,假设有 $k$ 个,总点数是 $n$,然后安排一下这些点的访问顺序,则答案就是 $\sum\limits_{i=k}^n k \times i \times A_{i-1}^{m-1} \times (n-k)!$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// luogu-judger-enable-o2 #include <bits/stdc++.h> #define LL long long #define rep(i, s, t) for(register int i = (s), i##end = (t); i <= i##end; ++i) #define dwn(i, s, t) for(register int i = (s), i##end = (t); i >= i##end; --i) using namespace std; 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 L, R; const int maxn = 1e7 + 10, mod = 1e9 + 7; int pri[maxn], tot, chk[maxn]; int fac[maxn], ifac[maxn]; inline int ksm(int x, int t, int res = 1) { for(; t; x = 1LL * x * x % mod, t = t >> 1) if(t & 1) res = 1LL * x * res % mod; return res; } void sieve() { rep(i, L, R) if(!chk[i]) { pri[++tot] = i; for(register int j = i + i; j <= R; j += i) chk[j] = 1; } } inline int A(int x, int y) { if(y > x) return 0; return 1LL * fac[x] * ifac[x - y] % mod; } int main() { //freopen("1.in","r",stdin); //freopen("1.out","w",stdout); L = read(), R = read(); sieve(); fac[0] = 1, ifac[0] = ifac[1] = 1; rep(i, 1, R) fac[i] = 1LL * fac[i - 1] * i % mod; rep(i, 2, R) ifac[i] = 1LL * (mod - (mod / i)) * ifac[mod % i] % mod; rep(i, 1, R) ifac[i] = 1LL * ifac[i] * ifac[i - 1] % mod; int ans = 0; rep(i, tot, R - L + 1) ans = (ans + 1LL * i * A(i - 1, tot - 1)) % mod; cout << 1LL * (1LL * ans * fac[R - L + 1 - tot] % mod) * tot % mod << endl; }