ZR模拟赛要题记录

（NOIP十连测、考前二十连测）

NOIP_Day5_合并石子(Continuing)

f_i 表示至少有 i 对小于等于 x 的方案数，
g_i 表示恰好有 i 对小于等于 x 的方案数。

g_i = \sum_{j_i} ^ {j <= n} f_j * \binom{j}{i}

dp_{i,j} 表示匹配了a的前i个数，至少存在j对和小于等于x

20连测_Day1_多项式题

$$d_i = \sum_{j = 1} ^ {j < i} d_j * num_{j + 1, i} = \sum_{j = 1} ^ {j < i} (d_j * (mulum[i] - mulum[j] * 10^{i - j})) = \sum(d_j * mulum[i] - d_j * mulum[j] * 10^{i - j})$$

$$D_i = \sum_{j = 1} ^ {j <= i} d_i \\ Sj_i = \sum_{j = 1} ^ {j <= i} mulum[j] * Teninv[j] * d[j]$$

int n;
ll d[maxn], mulum[maxn], Ten[maxn], D[maxn], Sj[maxn], Teninv[maxn], inv10;
char s[maxn];
ll ksm(ll bs, int B){
ll res(1);
while(B){
if(B & 1){
res = res * bs % Mod;
}
bs = bs * bs % Mod;
B >>= 1;
}
return res;
}
int main(){
n = rd();
scanf("%s", s + 1);
Teninv[0] = D[0] = Ten[0] = d[0] = 1ll; inv10 = ksm(10, Mod - 2);
for(int i(1); i <= n; ++i) Ten[i] = Ten[i - 1] * 10 % Mod, Teninv[i] = Teninv[i - 1] * inv10 % Mod, mulum[i] = (mulum[i - 1] * 10 + s[i] - '0') % Mod;
for(int i(1); i <= n; ++i){
d[i] = (D[i - 1] * mulum[i] % Mod - Sj[i - 1] * Ten[i] % Mod + Mod) % Mod;
D[i] = D[i - 1] + d[i]; Sj[i] = Sj[i - 1] + mulum[i] * Teninv[i] % Mod * d[i] % Mod;
if(D[i] >= Mod) D[i] -= Mod; if(Sj[i] >= Mod) Sj[i] -= Mod;
}
printf("%lld\n", d[n]);
return 0;
}



20连测_Day2_数集

1. 向集合 S 种加⼊⼀个数 x;
2. 对于⼀个数字 y ，查询$$\max_{x\in S} x op y$$ ，其中 op 是与、或、异或三种运算中的某⼏种。
（x 值域是 $$1 ^ 20$$
• op = xor

• op = and

• op = or

枚举子集

for(int i = x; i; i = (i - 1) & x);


代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int rd(){
int res = 0, fl = 1; char c = getchar();
while(!isdigit(c)){if(c == '-') fl = -1; c = getchar();}
while(isdigit(c)){res = (res << 3) + (res << 1) + c - '0'; c = getchar();}
return res * fl;
}
const int maxn = 1050010;
int ans1, ans2, q, op, x, trie[2][maxn * 20], cnt, vis[maxn];
int Max(int A, int B){
if(A < B) return B;
return A;
}
void insert(int num){
int k = 0;
for(int i(20); i >= 0; --i){
int b1 = (num >> i) & 1;
if(!trie[b1][k]) trie[b1][k] = ++cnt;
k = trie[b1][k];
}
}
int queryxor(int num){
int k(0), res(0);
for(int i(20); i >= 0; --i){
int b1 = (num >> i) & 1;
if(trie[!b1][k]){
res += (1 << i);
k = trie[!b1][k];
}
else k = trie[b1][k];
}
return res;
}
int main(){
//	freopen("B2.in", "r", stdin);
q = rd();
for(int i(1); i <= q; ++i){
op = rd(), x = rd();
if(op == 1){
insert(x);
if(!vis[x])
for(int i(x); i; i = (i - 1) & x)	vis[i] = 1;
continue;
}
if(op == 3){
printf("%d\n", queryxor(x)); continue;
}
ans1 = ans2 = 0;
for(int i(20); i >= 0; --i){
if(x & (1 << i)){//当前为1，&希望为1，| 无所谓
if(vis[ans1 | (1 << i)]) ans1 |= (1 << i);
}
//当前为0，& 无所谓，|希望为1
else if(vis[ans2 | (1 << i)]) ans2 |= (1 << i);
}
printf("%d %d %d\n", queryxor(x), ans1, ans2 | x);
}
return 0;
}


20连测_Day3_A

预处理 popcount

for(int i(1);i<len;++i){pct[i]=pct[i-lowbit(i)]+1;}


for(int x(1);x<=n;++x) popct[x] = popct[x >> 1] + (x & 1);


内存过大，数组访问不连续，不如O(n)跑函数

unsigned pct[35000000];
inline void dfs(unsigned pos,unsigned cnt,unsigned sta){
if(pos==n){++ans[cnt]; return;}
dfs(pos+1,cnt,sta);
dfs(pos+1,cnt+pct[sta&G[pos]],sta|(1<<pos));
}
int main(){
...
for(unsigned i(1);i<(1<<n);++i) pct[i]=pct[i>>1]+(i&1);
dfs(0,0,0);
...
}


inline int pct(unsigned x){
unsigned res(0);
for(;x;x-=lowbit(x)) res++;
return res;
}
inline void dfs(unsigned pos,unsigned cnt,unsigned sta){
if(pos==n){++ans[cnt]; return;}
dfs(pos+1,cnt,sta);
dfs(pos+1,cnt+pct(sta&G[pos]),sta|(1<<pos));
}
int main(){
dfs(0,0,0);
}


20连测_Day3_B[莫反板题]

$$\sum_{i=l}^{i<=r}[gcd(a_i,x)==1] \\= \sum_{i=l}^{i<=r}\sum_{d|gcd(a_i,x)}\mu(d) \\= \sum_{i=l}^{i<=r}\sum_{d|x,d|d_i}\mu(d) \\= \sum_{d|x}\sum_{i=l}^{i<=r}[d|a_i]*\mu(d)$$

$$\mu(x)=1 if (x == 1);\\=(-1)^k if (x 无平方因数且 x=\Pi_{i=1}^{i<=k}p_i)\\0 else$$

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int rd(){
int res = 0, fl = 1; char c = getchar();
while(!isdigit(c)){if(c == '-') fl = -1; c = getchar();}
while(isdigit(c)){res = (res << 3) + (res << 1) + c - '0'; c = getchar();}
return res * fl;
}
const int maxn = 100010;
int mu[maxn];
unsigned not_pri[maxn], pri[maxn], top, q, n, m, l, r, x, a[maxn], cnt[maxn], ans[maxn];
unsigned sum[1010][maxn];
map<pair<int, int>, int> Cnt;
int Gcd(int A, int B){
return B ? Gcd(B, A % B) : A;
}
int cal(int x){
int i, ans(0);
for(i=1;i*i<x;++i){
if(!(x%i)){
ans+=mu[i]*cnt[i], ans+=mu[x/i]*cnt[x/i];
}
}
if(i*i==x){
ans+=mu[i]*cnt[i];
}
return ans;
}
void getmu(){
mu[1]=1;
for(int i(2);i<=100000;++i){
if(!not_pri[i]){
pri[++top]=i; mu[i]=-1;
}
for(int j(1);j<=top&&i*pri[j]<=100000;++j){
not_pri[i*pri[j]]=1;
if(i%pri[j]==0){
mu[i*pri[j]]=0;break;
}
mu[i*pri[j]]=-mu[i];
}
}
}
struct Query{
int r,x,id,t;
Query(){}
Query(int R,int X,int Id,int T){
r=R,x=X,id=Id,t=T;
}
bool operator < (Query Q) const {
return this->r < Q.r;
}
}qry[maxn*2];
int main(){
getmu();
n = rd(),m = rd();
for(int i(1); i <= n; ++i) a[i]=rd();
for(int i(1); i <= m; ++i){
l=rd()-1,r=rd(),x=rd();
qry[++q]=Query(l,x,i,-1), qry[++q]=Query(r,x,i,1);
}
sort(qry+1,qry+1+q);
int c1(1);
for(int i(0);i<=n;++i){
int x;
for(x=1;x*x<a[i];++x){
if(!(a[i]%x)) cnt[x]++,cnt[a[i]/x]++;
}
if(x*x==a[i]) cnt[x]++;
while(c1<=q&&qry[c1].r<=i){
ans[qry[c1].id]+=qry[c1].t*cal(qry[c1].x);
c1++;
}
}
for(int i=1;i<=m;++i) printf("%d\n",ans[i]); printf("\n");
return 0;
}
//sqrt(100000)=316 3e7
//pri_100000 = 1e4
//1e5*1e4/64=1.6e7


NOIp_Day6_买

$$\rm a$$ 个得到 $$b$$ 元钱，实际花费 $$\rm (a*x-b)$$ 元钱，如果 $$a * x - b <= 0$$，我就可以无限买。

$$n/(a*x-b)$$ 表示买多少个 $$a$$，但是当我的钱不够买 $$\rm a$$ 个但是却能花费 $$\rm a * x - b$$ 时是不能买的，所以先买 $$(n-a*x)/(a*x-b)$$ 个。【1】

$$rest=(n-a*x)\mod(a*x-b)$$ 在 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int rd(){
int res = 0, fl = 1; char c = getchar();
while(!isdigit(c)){if(c == '-') fl = -1; c = getchar();}
while(isdigit(c)){res = (res << 3) + (res << 1) + c - '0'; c = getchar();}
return res * fl;
}
int T;
ll ans, a, b, x, n, money;
int main(){
T=rd();
for(int t(1);t<=T;++t){
ans=0;n=rd(),x=rd(),a=rd(),b=rd();
if(n<x){
printf("0\n"); continue;
}
if(n>=x*a&&a*x<=b){
printf("-1\n"); continue;
}
if(b==0){
ans=n/x;
printf("%lld\n",ans); continue;
}
if(n<a*x){
printf("%lld\n",n/x); continue;
}
ll l=(n-a*x)/(a*x-b);
money=n-l*(a*x-b);
ll l_=money%(a*x);
ll l__=l_/(a*x-b)+1;
money-=l__*(a*x-b);
ans=l*a+l__*a+money/x;
printf("%lld\n", ans);
}
return 0;
}
/*
5
100000000 2 1 1
1000000000 100 2 99
6651238 99 2 186
100000000 100000000 2 100000001
594580363 1 249195783 241474592
*/


NOIp_Day7_C

$$设 f(x)=\sum_{d|x} (\mu(\frac{x}{d}) * d)$$

$$那么 gcd(x, y)=\sum_{d|x,d|y} f(d)$$

$$gcd(x,y)=\sum_{d|x,d|y}(\sum_{g|d}(\mu(\frac{d}{g}) * g))$$

Cal() 计算$$\sum_{i}( (i,j) 被包含的区间数量)$$

$(i-1)*(i-2)/2+i-1 + [(n-j)*(n-j-1)/2+n-j]\ast cnt + [(j-i-1)*(j-i-2)/2+j-i-1] = i*i/2-i/2 + [n*(n-1)/2+(1-j-n*2)*j/2]*cnt + j*j/2-i*j+i*i/2-3*j/2+3*i/2+1+j-i-1 = i*i + j*j/2 - i*j - (1/2+cnt/2)*j - n*cnt*j - 1$

Ans += Cal(j, d) * f[d];

20连测_Day9_B_操作数列

1. a[i]=b
2. a[i]+=b
3. a[i]*=b
其中 $$i,b$$ 是给定的，每个操作只能用一次，最多使用 $$m$$ 个操作，让整个数列的乘积最大。

实际操作

• 覆盖操作：对于每一个 $$a[i]$$ 最多进行一次覆盖操作，也就是那个最大的 $$b$$
• 增加操作：增加操作一定是在覆盖操作之后进行的，不然就白覆盖了。优先选择 $$b$$ 大的加操作
• 乘操作：乘操作一定放在加操作之后，优先选择 $$b$$ 大的乘操作

$$Pi=\Pi_{i=1}^{k} a_i$$，考虑进行一次乘操作之后，$$Pi$$ 要乘上 $$b$$

贪心

#include<bits/stdc++.h>
#define Mod 1000000007
using namespace std;
typedef long long ll;
int rd(){
int res = 0, fl = 1; char c = getchar();
while(!isdigit(c)){if(c == '-') fl = -1; c = getchar();}
while(isdigit(c)){res = (res << 3) + (res << 1) + c - '0'; c = getchar();}
return res * fl;
}
const int maxn = 1000010;
double Tmp;
ll del;
int id;
struct MUL{
ll Mul;
int id;
}mul[maxn];
int k, n, m, T, I, tot, top, cnt, Tot;
ll a[maxn], cov[maxn], Sum[maxn], ans(1), B;
return A.del>B.del;
}
return A.Tmp>B.Tmp;
}
struct Op{
double Tmp;
int id, tp, pos;
}Ans[maxn];
bool operator < (Op A, Op B){
return A.Tmp < B.Tmp;
}
priority_queue<Op> Q;
int main(){
k=rd(), n=rd(), m=rd();
for(int i(1);i<=k;++i) a[i]=rd();
for(int i(1);i<=n;++i){
T=rd(),I=rd(),B=rd();
if(T==1) cov[I]=max(cov[I],B);
else mul[++top]={B,I};
}
for(int i(1);i<=top;++i) Q.push(Op{mul[i].Mul,mul[i].id,3,i});
for(int i(1);i<=k;++i) if(cov[i]){
}
for(int i(1);i<=k;++i) Sum[i]=a[i];
for(int i(1);i<=tot;++i){
}
while(Q.size()){
Op u=Q.top(); Q.pop();
if(u.Tmp <= 1) break;
cnt++;
Ans[++Tot]=u;
if(cnt>=m) break;
}
for(int i(1);i<=Tot;++i){
if(Ans[i].tp==2){