今天的题主要是考思维(这只是我考的差的借口罢了)
T1
这道题其实很简单,只要分析出了一个数它的得分是它的好素数数量之和减去坏素数数量之和就好了。然后我们计算出[1,i]区间总的gcd,判断当前gcd的价值,若gcd价值小于0 就用当前的ans-val(tgcd[i])*i就行了, gcd[i] 表示区间[1,i]的gcd 其实就是一个贪心思想
#include#include #include #include #include using namespace std; const int maxn=50010; int sushu[maxn],bad[maxn],a[maxn]; int n,m,tot,flag[maxn],x; int tgcd[maxn],cost; bool visit[maxn]; bitset<1000000000> bj; //与bool用法类似 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*10+ch-'0'; ch=getchar(); } return x*f; } void prepare_work() { visit[0]=visit[1]=true; int nmsl=sqrt(1e9); for(int i=2;i<=nmsl;i++) { if(!visit[i]) sushu[++tot]=i; for(int j=1;j<=tot && sushu[j]*i<=nmsl;j++) { flag[sushu[j]*i]=true; if(i%sushu[j]==0) break; } } } int calc(int x) { int i=1,ans=0; for(int i=1;i<=tot && sushu[i]*sushu[i]<=x;i++) { while(x%sushu[i]==0) { x/=sushu[i]; ans+=bj[sushu[i]]==true?-1:1; } } if(x>1) bj[x]?ans--:ans++; return ans; } int main() { prepare_work();//欧拉筛素数 n=read();m=read(); a[1]=read();tgcd[1]=a[1];//tgcd表示先前所有数一起的gcd for(int i=2;i<=n;i++) { a[i]=read(); tgcd[i]=__gcd(tgcd[i-1],a[i]);//__gcd是一个库函数 } for(int i=1;i<=m;i++) x=read(),bj[x]=true;//bj表示当前数是否是坏素数 for(int i=1;i<=n;i++) cost+=calc(a[i]);//计算一个数的f值,相当于计算其好素数个数-坏素数个数 int pre=1;//pre表示已经除了的数 for(int i=n;i>=1;i--) { tgcd[i]/=pre; if(tgcd[i]==1) continue; int num=calc(tgcd[i]);//计算当前gcd的值 if(num<0)//如果小于零,说明删去当前gcd可以增加分数 { cost-=i*(num);//删去当前gcd pre*=tgcd[i];//更新已经除了的数 } } printf("%d",cost); return 0; }
T2
这道题就是倍增加哈希(说的好像很简单一样)
那么我来详细讲一讲
pre_-bz[i]预处理2^i mi[i]预处理w^i,其中w表示哈希时的base
g[j][i]表示i向后跳2^j步z的值 f[j][i]表示i向后跳2^j步得到的当前01串的哈希值
注意事项:f[j][i]表示的是从i已经向后跳2^j步得到的z值向后跳2 的(j-1)次方得到的hash值。f[j-1][i]表示i向后跳2的(j-1)次方得到的hash值,把f[j-1][i]*base与f[j-1][g[j-1][i]]相加就是f[j][i]的hash值 确实很绕 但是事实就是如此 尝试分析当前hash=原串*base+新加入的字符 ,这里原串就是原串,但因为新加入的不是字符,而是串且长度为2的j-1次方,那么我们会将原串 * base的2的j-1次方
#include#include #include #include #define int long long using namespace std; const int maxn=100050,w=31,mod=10890604; int pre_bz[30],g[20][maxn*10],f[20][maxn*10]; int a,c,k,m,n,mid; int num[maxn],s,mi[maxn]; int ans=0,tot=0; char chuan[maxn]; inline bool check(int x) { int t=0; for(int i=16;i+1;i--) { if(n&pre_bz[i]) { t=(t*mi[pre_bz[i]]%mod+f[i][x])%mod; x=g[i][x]; } } return t==s; } signed main() { scanf("%lld %lld %lld %lld %lld",&a,&c,&k,&m,&n); char ch=getchar(); while(ch<'0' || ch>'1') ch=getchar(); while(ch>='0' && ch<='1') num[++tot]=ch-'0',ch=getchar(); pre_bz[0]=1;mi[0]=1; for(register int i=1;i<20;i++) pre_bz[i]=pre_bz[i-1]<<1; for(register int i=1;i<=n;i++) { s=s*w%mod+num[i]; mi[i]=mi[i-1]*w%mod; } mid=m>>1; for(register int i=0;i ) { g[0][i]=((a*i+c)/k)%m; f[0][i]=(g[0][i]>=mid)?1:0; } for(register int j=1;j<=16;j++) { for(register int i=0;i ) { g[j][i]=g[j-1][g[j-1][i]]; f[j][i]=(f[j-1][g[j-1][i]]+f[j-1][i]*(mi[pre_bz[j-1]])%mod)%mod; } } for(int i=0;i ) ans+=(check(i))?1:0; printf("%lld",ans); }
T3
二分套二分
1.我们先二分到第一个但单价小于当前钱的,从它开始买
2.然后二分下一个点使得其前缀和刚好小于剩余的钱
3.将l+1~n所在的所有的东西全部买了后再算一下l能买多少个
3.剩余的钱再进行以上操作
emmm T3就是这么种考法。不考数据结构,不考图论,考二分套二分,醉了
#include#include #include #include #define int long long using namespace std; const int maxn=100010; struct node{ int val,num; #define val(x) item[x].val #define num(x) item[x].num }item[maxn]; int n,m,sum[maxn],sumnum[maxn],left,k; inline bool cmpl(node a,node b) { return a.val<b.val; } signed main() { int i,j,l,r; scanf("%lld %lld",&n,&m); for(int i=1;i<=n;i++) scanf("%lld %lld",&val(i),&num(i)); sort(item+1,item+1+n,cmpl); for(int i=1;i<=n;i++) sum[i]=sum[i-1]+val(i)*num(i),sumnum[i]=sumnum[i-1]+num(i); while(m--) { scanf("%lld",&k); for(i=n,j=0;i>0&&k>=item[1].val;)//边界条件 剩余的钱一定要至少买得起最便宜的物品,否则就输出 { l=1;r=i; while(l<r) { int mid=(l+r+1)>>1; if(k>=item[mid].val) l=mid; else r=mid-1; } i=l; if(k>=sum[i]) { j+=sumnum[i]; break;//若当前你买的起所有的单价小于你钱的物品 直接输出 } l=1;r=i; while(l<r) { int mid=(l+r)>>1; if(k>=sum[i]-sum[mid]) r=mid; else l=mid+1; } k-=sum[i]-sum[l]; j+=sumnum[i]-sumnum[l]+k/item[l].val; k%=item[l].val; i=l-1; } printf("%lld\n",j); } }
总结今天的考试考思维的部分挺多,所以暴力其实也就显得尤为重要,部分分也是很可观的分数