CSP-S 模拟 19/10/11

洛阳怀
首先发现取前缀 g c d gcd gcd的顺序一定是从后往前,然后就没有什么思路了,考虑从 f f f 的性质入手
f ( s ) = f ( s p ) + 1 / − 1 f(s)=f(\frac{s}{p})+1/-1 f(s)=f(ps)+1/1,然后 f ( s p ) f(\frac{s}{p}) f(ps) 又会取它的最小质因子
于是一个 s s s 的所有质因子对它都有贡献,如果是好质数,有1的贡献,否则为 -1 的贡献
除掉一个 g c d gcd gcd 就是把 1 − − i 1--i 1i中所有数的 f ( g c d ) f(gcd) f(gcd) 给去掉,贪心判一下即可

// woj 4150
#include<bits/stdc++.h>
#define N 1000005
using namespace std;
int n,m,a[N],isp[N],prim[N],tot;
int g[N],ans;
bitset<1000000000> S;
int read(){
	int cnt=0; char ch=0;
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch))cnt=cnt*10+(ch-'0'),ch=getchar();
	return cnt;
}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
void init(){
	for(int i=2;i<=N-5;i++){
		if(!isp[i]) isp[i]=1,prim[++tot]=i;
		for(int j=1;j<=tot && prim[j]*i<=N-5;j++){
			isp[i*prim[j]]=1;
			if(i%prim[j]==0) break;
		}
	} 
}
int divide(int x){
	int ans=0;
	for(int i=1;i<=tot && prim[i]*prim[i]<=x;i++){
		while(x%prim[i]==0) ans+=S[prim[i]]?-1:1,x/=prim[i];
	}
	if(x>1) ans+=S[x]?-1:1;
	return ans;
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=m;i++) S[read()]=1;
	init();
	for(int i=1;i<=n;i++) ans+=divide(a[i]);
	for(int i=1;i<=n;i++) g[i]=gcd(g[i-1],a[i]);
	int div=1;
	for(int i=n;i>=1;i--){
		g[i]/=div;
		int x=divide(g[i]);
		if(x<0) ans+=i*(-x),div*=g[i];
	}
	printf("%d\n",ans);
	return 0;
}

01 串
发现 m ≤ 1 e 6 m\le 1e6 m1e6于是就可以枚举每一个 z z z
于是预处理出每一个 z z z 向后跳的结点,然后倍增
考虑如何跟给出的 01 01 01 串匹配,可以 h a s h hash hash,倍增的时候顺便处理即可

// woj 4079 
#include<bits/stdc++.h>
#define N 1000050
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;
}
typedef unsigned long long ull;
const ull Base = 5261023;
ull S[N]; char s[N];
ull hash[N][18], pw[N];
int nxt[N][18];
int a, c, k, m, n;
ull get(int l, int r){ return S[r] - S[l-1] * pw[r - l + 1];}
int main(){
	a = read(), c = read(), k = read(), m = read(), n = read();
	scanf("%s", s + 1); pw[0] = 1;
	for(int i = 1; i <= n; i++) pw[i] = pw[i-1] * Base, S[i] = S[i-1] * Base + s[i] - '0';
	for(int i = 0; i < m; i++){
		nxt[i][0] = ((1ll * i * a + c) / k) % m;
		hash[i][0] = nxt[i][0] < (m/2) ? 0 : 1;
	}
	for(int i = 1; (1 << i) <= n; i++){
		for(int j = 0; j < m; j++){
			nxt[j][i] = nxt[nxt[j][i-1]][i-1];
			hash[j][i] = hash[j][i-1] * pw[1 << (i-1)] + hash[nxt[j][i-1]][i-1];
		}
	} int ans = 0;
	for(int i = 0; i < m; i++){
		int pos = 1;
		int x = i;
		for(int j = 18; j >= 0; j--){
			if(pos + (1 << j) - 1 > n) continue;
			if(hash[x][j] == get(pos, pos + (1 << j) - 1)) x = nxt[x][j], pos += (1 << j);
		} if(pos == n + 1) ++ ans;
	} cout << ans; return 0;
}

shop
发现并没有什么数据结构支持这个操作,于是我们想到了乱搞
如果一个一个减的话,显然会 T T T,如何优化
首先当前价位最多可以买的是 w v i \frac{w}{v_i} viw,但万一钱太多,就会一个一个往后跳
想到了求一个前缀和然后二分下一个跳的位置,还需要记录个数的前缀和
整理一下思路
1.我们先二分到第一个小于它的,显然要从它开始买
2.然后二分下一个点使得直接的前缀和刚好小于它
3.减去前缀和过后用 w v i \frac{w}{v_i} viw处理一下当前能买的个数
4.剩下的钱递归处理
听说 w w w跳的操作类似取模,每次至少少一半,我试了下好像不是,复杂度不能严格证明
但一想到我们每次可以通过前缀和剪掉一大坨,然后将 w w w 的规模缩小到较小的 v i v_i vi,我们就有信心认为我们的程序快得飞起,复杂度 O ( n l o g n k ) O(nlognk) O(nlognk) k k k 是递归次数,大概是 l o g ( w ) log(w) log(w) 级别的
所以以后一道题没有思路并不要怕,从出题人的角度看他并卡不掉你

// woj 2821
#include<bits/stdc++.h>
#define N 100050
using namespace std;
typedef long long ll;
ll read(){
	ll 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;
}
int n, m;
struct data{ int x, v;} a[N];
bool cmp(data a, data b){ return a.v > b.v;}
ll sum[N], cnt[N];
ll get(int laspos, ll w){
	if(w < a[n].v) return 0;
	ll ans = 0;
	int l = laspos, r = n + 1;
	while(l < r){
		int mid = (l+r) >> 1;
		if(a[mid].v <= w) r = mid;
		else l = mid + 1;
	} 
	int pos = l;
	l = pos - 1; r = n;
	while(l < r){
		int mid = (l+r+1) >> 1;
		if(sum[mid] - sum[pos-1] <= w) l = mid;
		else r = mid - 1;
	} int Rp = l;
	laspos = Rp;
	w -= sum[Rp] - sum[pos - 1];
	ans += cnt[Rp] - cnt[pos - 1];
	if(Rp == n) return ans;
	if(Rp < n){
		int k = w / a[Rp + 1].v;
		w -= 1ll * k * a[Rp + 1].v; ans += k;
		++laspos;
	} return ans + get(laspos, w);
}
int main(){
	n = read(), m = read();
	for(int i = 1; i <= n; i++) a[i].v = read(), a[i].x = read();
	sort(a + 1, a + n + 1, cmp);
	for(int i = 1; i <= n; i++){
		sum[i] = sum[i-1] + 1ll * a[i].v * a[i].x;
		cnt[i] = cnt[i-1] + (ll)a[i].x;
	}
	while(m--){
		ll w = read();
		cout << get(1, w) << '\n';
	} return 0;	
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值