NOIP赛前模拟20171027总结
题目:
1.寿司
给定一个环形的RB串··要求经过两两互换后RB分别形成两段连续区域,求最少操作次数(算法时间O(n))
2.金字塔
给定一个金字塔的侧面图有n层··已知每一层的宽度··高度均为1··要求在图中取出恰好K个互不相交的矩形(边缘可以重叠),求最多可以取出多少面积
n<=20000,k<=100
3.心灵治愈
给定n,m要求取出不大于m的n个正整数,问有多少种取法使n个数和m的最大公因数为1,n,m<=10^15
题解:
1.分析
首先为了方便我们把环从中间断开看成一个序列,我们考虑如果移动R串··那么肯定是找到某一B为中心··B的左边R移到一起··B的右边R移到一起(这里描述有点模糊···如果序列左边的R都移到一起,且序列最左边为R,右边同理··则实际在环中R肯定是连续的一段··)
因此我们先随意找一个B为中心··然后计算答案··之后我们一次将序列最左端的字符移到最右端(比如序列BBRRR移动后就变成BRRRB)然后考虑答案的变化即可···具体实现参见代码
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; const int N=2e6+5; int T,n,num[N]; char s[N]; int main() { //freopen("a.in","r",stdin); //freopen("a.out","w",stdout); scanf("%d",&T); while(T--) { long long ans=0,cnt=0; int tot1=0,tot2=0,totl=0,totr=0,mid; scanf("%s",s+1);n=strlen(s+1); for(int i=1;i<=n;i++) { if(s[i]=='B') num[i]=num[i+n]=1,tot1++; else num[i]=num[i+n]=2,tot2++; } int half=(tot1+1)/2;int temp=0; for(int i=1;i<=n;i++) { if(num[i]==1) {temp++;if(temp==half) mid=i;} else { if(temp<half){totl++;cnt+=temp;} else {totr++;cnt+=(tot1-temp);} } } ans=cnt; for(int i=1;i<n;i++) { if(num[i]==1) { int tot=0,j; for(j=mid+1;num[j]!=1;j++) tot++; cnt-=totl;cnt+=(totr-tot);mid=j; totl+=tot;totr-=tot; if(tot1%2==0) cnt-=tot; ans=min(ans,cnt); } else { totl-=1;totr+=1; } } cout<<ans<<endl; } return 0; }
2.dp+决策单调性/斜率优化
dp方程肯定很好想··第一要明确取的方案··我们肯定是以某一层的宽度为矩形的一边··然后往下取到某一层为一个矩形·矩形的高就是两层高的差··
然后设f[j][i]为第j块矩形以第i层为一边的最大面积··转移方程即为:
f[j][i]=max(f[j][i],f[k][i-1]+(long long)len[j]*(j-k));
其中k为我们往下枚举的层数··len为j层的宽度··
然后通过打表(dalao也可以分析)得出该方程满足决策单调性且使用于斜率优化····这里两种方法都能过
决策单调性:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<string> #include<cstring> #include<algorithm> using namespace std; inline int R() { char c;int f=0; for(c=getchar();c<'0'||c>'9';c=getchar()); for(;c<='9'&&c>='0';c=getchar()) f=(f<<3)+(f<<1)+c-'0'; return f; } const int N=20005; const int M=105; struct node { int l,r,pos; }Que[N]; int n,K; long long len[N],f[N][M]; inline long long calc(int i,int j,int now) { return f[j][now-1]+(long long)len[i]*(i-j); } inline int find(node a,int b,int now) { int le=a.l,ri=a.r,ans=a.r+1; while(le<=ri) { int mid=(le+ri)/2; if(calc(mid,b,now)>calc(mid,a.pos,now)) ri=mid-1,ans=mid; else le=mid+1; } return ans; } inline void dp(int now) { int Head=1,Tail=0; node tmp;tmp.l=now;tmp.r=n;tmp.pos=now-1;Que[++Tail]=tmp; for(int i=now;i<=n;i++) { while(Que[Head].r<i) Head++; f[i][now]=calc(i,Que[Head].pos,now); if(calc(n,i,now)>calc(n,Que[Tail].pos,now)) { while(Head<=Tail&&calc(Que[Tail].l,i,now)>calc(Que[Tail].l,Que[Tail].pos,now)) Tail--; if(Head<=Tail) { int t=find(Que[Tail],i,now); Que[Tail].r=t-1; node tmp;tmp.l=t,tmp.r=n,tmp.pos=i;Que[++Tail]=tmp; } else { node tmp;tmp.l=i+1;tmp.r=n;tmp.pos=i;Que[++Tail]=tmp; } } } } int main() { //freopen("pyramid.out","w",stdout); n=R(),K=R();int x,y; if(n<=1000) { for(int i=1;i<=n;i++) { x=R(),y=R();len[i]=y-x+1;f[i][1]=(long long)len[i]*i; } for(int i=2;i<=K;i++) for(int j=i;j<=n;j++) for(int k=i-1;k<j;k++) f[j][i]=max(f[j][i],f[k][i-1]+(long long)len[j]*(j-k)); long long ans=0; for(int i=K;i<=n;i++) ans=max(f[i][K],ans); cout<<ans<<"\n"; } else { for(int i=1;i<=n;i++) { x=R(),y=R();len[i]=y-x+1;f[i][1]=(long long)len[i]*i; } for(int i=2;i<=K;i++) dp(i); long long ans=0; for(int i=K;i<=n;i++) ans=max(f[i][K],ans); cout<<ans<<"\n"; } return 0; }
3.质因数分解+容斥原理
这道题和之前跳蚤的那道题是一模一样的··这里就并不多说了··
唯一注意的是我发现了自己快速幂的一个漏洞··求a^b之前要将a取模···之前一直没有注意到···还有就是注意最后答案为负的问题
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> #include<vector> using namespace std; vector<long long>zhiyinzi; const long long mod=1e9+7; long long n,m,ans=0; inline long long ksm(long long a,long long b) { long long ans=1;a%=mod; while(b) { if(b%2==1) ans=(ans*a)%mod; b/=2;a=(a*a)%mod; } return ans; } inline void dfs(int u,long long tot,long long f) { if(u==zhiyinzi.size()) { long long temp=m/tot; ans=(ans+f*ksm(temp,n))%mod; ans=(ans%mod+mod)%mod; return; } dfs(u+1,tot,f); dfs(u+1,tot*zhiyinzi[u],-f); } int main() { scanf("%I64d%I64d",&n,&m); long long temp=m; for(long long i=2;i*i<=m;i++) { if(i>temp) break; if(temp%i==0) { while(temp%i==0) temp/=i; zhiyinzi.push_back(i); } } if(temp!=1) zhiyinzi.push_back(temp); dfs(0,1,1); ans=(ans%mod+mod)%mod; cout<<ans<<endl; return 0; }