cf round 422
A
题目要求
求两个数阶乘的gcd
参考AC代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| const LL mod=1e9+7; const LL INF=(LL)1e18; using namespace std; int main(){ // freopen("input.txt","r",stdin); // freopen("ouput.txt","w",sdout); ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); LL a,b; cin>>a>>b; LL c=min(a,b); LL ans=1; while(c){ ans*=c; c--; } cout<<ans<<endl; return 0; }
|
思路
(max-min)!
B
题目要求
给出字符串s1和s2
要求最少改动s1中的多少个字母可以满足s1是s2的一个子串
参考AC代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| const LL mod=1e9+7; const LL INF=(LL)1e18; using namespace std; int main(){ // freopen("input.txt","r",stdin); // freopen("ouput.txt","w",sdout); ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); int n,m; cin>>n>>m; string s1,s2; cin>>s1>>s2; int ans=inf; int po=0; for(int i=0;i<m-n+1;i++){ int st=i,ed=st+n-1; int cur_po=0,cur_ans=0; for(int j=st;j<=ed;j++){ if(s2[j]!=s1[cur_po++]) cur_ans++; } if(cur_ans<ans){ ans=cur_ans; po=st; } } cout<<ans<<endl; int len=0; for(int i=po;i<=po+n-1;i++) if(s2[i]!=s1[len++]) cout<<len<<" "; cout<<endl; return 0; }
|
思路
用一个length1的窗口去滑动s2 记录下需要改变的字母的最小值和最小值的起始位置
最后判断下起始位置的子串 输出具体位置即可
C
题目要求
有n个区间 每个区间有左端点 右端点和cost值 输入需要满足的区间长度k
要从n个区间里选择若干个区间 满足他们互不相交 总的区间长度为k 并且cost和最小
参考AC代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| const LL mod=1e9+7; const LL inf=(LL)1e18; using namespace std; struct node{ LL st,interval,cost; int flag; node(LL st,LL interval,LL cost,int flag):st(st),interval(interval),cost(cost),flag(flag){} }; int cmp(node a,node b){ if(a.st==b.st) return a.flag>b.flag; return a.st<b.st; } vector<node>s; LL dp[maxn]; int main(){ // freopen("input.txt","r",stdin); // freopen("ouput.txt","w",sdout); ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); LL n,x; cin>>n>>x; for(int i=1;i<=n;i++){ LL l,r,c; cin>>l>>r>>c; s.pb(node(l,r-l+1,c,1)); s.pb(node(r,r-l+1,c,-1)); } for(int i=0;i<maxn;i++) dp[i]=inf; LL ans=inf; sort(s.begin(),s.end(),cmp); for(int i=0;i<s.size();i++){ if(s[i].flag==1){ LL temp=x-s[i].interval; if(temp<0) continue; ans=min(ans,dp[temp]+s[i].cost); } else dp[s[i].interval]=min(dp[s[i].interval],s[i].cost); } if(ans<inf) cout<<ans<<endl; else cout<<"-1"<<endl; return 0; }
|
思路
dp
dp[interval]表示之前所有点中满足区间长度是interval的最小cost值
每个区间拆分成两个端点 1代表左端点 -1代表右端点
所有的点升序排序 大小相同的点左端点优先
对于每个点 如果是左端点 更新ans值 如果是右端点 更新dp值
更新ans:temp表示除去该点剩下的区间长度 若temp>=0 dp[temp]即表示之前的状态中inetrval是temp的最小价值 加上该点的cost值更新答案
更新dp:如果到了右端点 说明某个区间已经更新好ans值 此时应该更新dp 用dp(之前的min)和该点的cost值取最小值即可
注意开LL
inf由于开了LL为1e18 不能取0x3f3f3f3f 所以不能用memset 要遍历初始化
D
题目要求
一场选美比赛有N个人,可以分成N/x,每组x人。每组的比较次数为x(x-1)/2,f[N]为最后决出冠军所需的比较次数,可以通过改变x的值使f[N]改变。
题目给出t,l,r(1 ≤ t < 109 + 7, 2 ≤ l ≤ r ≤ 5·106)。求 t^0⋅f(l)+t^1⋅f(l+1)+⋯+t^r−l⋅f(r) 的最小值对1e9+7的模。
参考AC代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| const LL mod=1e9+7; const LL INF=(LL)1e18; using namespace std; int vis[maxn+50]; LL prime[maxn+50],len=0; LL f[maxn+50]; void solve(){ mm(vis,0); vis[0]=vis[1]=1; int m=sqrt(maxn+0.5); for(LL i=2;i<=m;i++){ if(!vis[i]){ for(int j=i*i;j<=maxn;j+=i) vis[j]=1; } } for(LL i=0;i<maxn;i++){ if(!vis[i]) prime[len++]=i; } } int main(){ // freopen("input.txt","r",stdin); // freopen("ouput.txt","w",sdout); ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); solve(); mm(f,0); LL t,l,r; cin>>t>>l>>r; for(LL i=2;i<maxn;i++){ if(!vis[i]) f[i]=(i*(i-1)/2)%mod; else{ LL factor; for(LL j=0;j<len;j++){ if(i%prime[j]==0){ factor=prime[j]; break; } } f[i]=(i/factor*f[factor]+f[i/factor])%mod; } } LL ans=0; for(LL i=r;i>=l;i--){ ans=(ans*t)%mod; ans=(ans+f[i])%mod; } cout<<ans<<endl; return 0; }
|
思路
数论
如果人数为素数,那f[N]=N(N-1)/2;
如果不是素数,那就找出最小素因子x,分成N/x组,每组x人,f[N]=N/x*f[x]+f[N/x]。