[关闭]
@exut 2024-10-16T06:08:05.000000Z 字数 6285 阅读 150

HNFMS week6 任务

刷题


[CSP-J2019 江西] 非回文串

逆天数据范围,只开 发神经属于

逆天相同字母算不同方案

直接求好像不是很典,但是可以试试正难则反

任意摆放的总方案数是 ,毋庸置疑

考虑重排为回文的方案数

注意到字符间有区别,所以方案数为( 为该字符出现次数)

然后发现做完了已经

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define int long long
  4. const int mod=1e9+7;
  5. int s[26];
  6. signed main(){
  7. int n;cin>>n;
  8. int ans=1;
  9. for(int i=1;i<=n;i++){
  10. ans*=i;ans%=mod;
  11. char x;cin>>x;
  12. s[x-'a']++;
  13. }
  14. int cnt=0;
  15. for(int i=0;i<26;i++)if(s[i]%2==1)cnt++;
  16. if(cnt>1){
  17. cout<<ans;return 0;}
  18. else{
  19. long long cnt2=1;
  20. for(int i=1;i<=n/2;i++) cnt2*=i,cnt2%=mod;
  21. for(int i=0;i<26;i++){
  22. for(int j=s[i]/2+1;j<=s[i];j++){
  23. cnt2*=j;cnt2%=mod;
  24. }
  25. }
  26. cout<<(ans-cnt2+mod)%mod;
  27. }
  28. return 0;
  29. }

懒得写逆元了水一些复杂度 ,随便可以优化到线性

CF920F

看着很势能一道题,容易想到一个数的正约数个数不超过 ,然后类似花神势能线段树或者分块就行

每次处理约数好像有点慢,可以线性筛一下或者预处理出来

使用分块,

  1. #include<bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. const int N=3e5+5;
  5. int num[1000005];
  6. bool vis[1000005];
  7. int prime[1000005];
  8. int d[1000005];
  9. int mx[1000005];
  10. int cnt;
  11. void init(){
  12. num[1]=1;
  13. for(int i=2;i<=1000000;i++){
  14. if(!vis[i]){
  15. num[i]=2,d[i]=1;
  16. prime[++cnt]=i;
  17. }
  18. for(int j=1;j<=cnt and i*prime[j]<=1000000;j++){
  19. vis[i*prime[j]]=1;
  20. if(i%prime[j]==0){
  21. num[i*prime[j]]=num[i]/(d[i]+1)*(d[i]+2);
  22. d[i*prime[j]]=d[i]+1;
  23. break;
  24. }
  25. else{
  26. num[i*prime[j]]=num[i]*2;
  27. d[i*prime[j]]=1;
  28. }
  29. }
  30. }
  31. }
  32. int bl[N];
  33. int sum[N];
  34. int a[N];
  35. int B,n,m;
  36. int L[N],R[N];
  37. void modi(int l,int r){
  38. if(bl[l]==bl[r]){
  39. for(int i=l;i<=r;i++){
  40. a[i]=num[a[i]];
  41. }
  42. mx[bl[l]]=sum[bl[l]]=0;
  43. for(int i=L[bl[l]];i<=R[bl[l]];i++) mx[bl[l]]=max(mx[bl[l]],a[i]),sum[bl[l]]+=a[i];
  44. return ;
  45. }
  46. for(int i=l;bl[i]==bl[l];i++){
  47. a[i]=num[a[i]];
  48. }
  49. mx[bl[l]]=sum[bl[l]]=0;
  50. for(int i=L[bl[l]];i<=R[bl[l]];i++) mx[bl[l]]=max(mx[bl[l]],a[i]),sum[bl[l]]+=a[i];
  51. for(int i=r;bl[i]==bl[r];i--){
  52. a[i]=num[a[i]];
  53. }
  54. mx[bl[r]]=sum[bl[r]]=0;
  55. for(int i=L[bl[r]];i<=R[bl[r]];i++) mx[bl[r]]=max(mx[bl[r]],a[i]),sum[bl[r]]+=a[i];
  56. for(int yzy=bl[l]+1;yzy<bl[r];yzy++){
  57. if(mx[yzy]<=2) continue;
  58. for(int i=L[yzy];i<=R[yzy];i++){
  59. a[i]=num[a[i]];
  60. }
  61. mx[yzy]=sum[yzy]=0;
  62. for(int i=L[yzy];i<=R[yzy];i++) mx[yzy]=max(mx[yzy],a[i]),sum[yzy]+=a[i];
  63. }
  64. }
  65. int query(int l,int r){
  66. int ret=0;
  67. if(bl[l]==bl[r]){
  68. for(int i=l;i<=r;i++){
  69. ret+=a[i];
  70. }
  71. return ret;
  72. }
  73. for(int i=l;bl[i]==bl[l];i++){
  74. ret+=a[i];
  75. }
  76. for(int i=r;bl[i]==bl[r];i--){
  77. ret+=a[i];
  78. }
  79. for(int i=bl[l]+1;i<bl[r];i++){
  80. ret+=sum[i];
  81. }
  82. return ret;
  83. }
  84. signed main(){
  85. init();
  86. cin>>n>>m;
  87. int B=sqrt(n);
  88. for(int i=1;i<=n;i++){
  89. bl[i]=(i-1)/B+1;
  90. R[bl[i]]=i;
  91. if(!L[bl[i]]) L[bl[i]]=i;
  92. cin>>a[i];
  93. sum[bl[i]]+=a[i];
  94. mx[bl[i]]=max(mx[bl[i]],a[i]);
  95. }
  96. while(m--){
  97. int l,r,opt;
  98. cin>>opt>>l>>r;
  99. if(opt==1){
  100. modi(l,r);
  101. }
  102. else{
  103. cout<<query(l,r)<<"\n";
  104. }
  105. }
  106. }

CF1870E

这个众所周知MEX和异或和都不是什么好维护的东西,考虑上dp试试

虽然题目问的是一个最优化问题,但是这个最优化啊看着不是很可做,我们不妨考虑转化成判定性问题:设 表示在 之前,能否凑出异或和为

我们首先可以 地预处理求出任意区间的MEX

(需要注意的是答案的上界大概在 的样子,所以数组第二维开两倍~)

于是我们可以写出一个暴力dp

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=5005;
  4. int f[N][N*2];
  5. int t,n;
  6. int ton[N*2];
  7. int mex[N][N];
  8. int a[N];
  9. int main(){
  10. cin>>t;
  11. while(t--){
  12. cin>>n;
  13. for (int i=1;i<=n;i++)
  14. for(int j=0;j<=n*2;j++)
  15. mex[i][j]=N,f[i][j]=0;
  16. for(int i=1;i<=n;i++){
  17. cin>>a[i];
  18. }
  19. f[0][0]=1;
  20. for(int l=1;l<=n;l++){//init mex
  21. int x=0;
  22. for(int r=l;r<=n;r++){
  23. ton[a[r]]++;
  24. while(ton[x]) x++;
  25. mex[l][r]=x;
  26. }
  27. for(int r=l;r<=n;r++){
  28. ton[a[r]]--;
  29. }
  30. }
  31. for(int i=1;i<=n;i++){
  32. for(int j=1;j<=i;j++){
  33. for(int k=0;k<=2*n;k++){
  34. f[i][k]|=f[j-1][k^mex[j][i]];
  35. }
  36. }
  37. }
  38. for(int i=2*n;i>=0;i--){
  39. if(f[n][i]){cout<<i<<"\n";break;}
  40. }
  41. }
  42. }

这个转移式子的异或导致完全不能用数据结构来优化这个dp,我们也没法贪心...吗?

欸贪心,不妨考虑一下什么情况我们可以舍弃一个区间:

,如果 使得 并且 ,那么我们必定不会选取 ,选取 一定是更优的

容易得出,对于每个 至多有一个 使得 是一个好区间,反之同样

复杂度

  1. #include<bits/stdc++.h>
  2. #define pii pair<int,int>
  3. #define mp make_pair
  4. using namespace std;
  5. const int N=5015;
  6. bool f[N][N*2];
  7. int t,n;
  8. int ton[N*2];
  9. int a[N];
  10. vector<pii> g[N];
  11. int main(){
  12. cin>>t;
  13. while(t--){
  14. cin>>n;
  15. for(int i=1;i<=n;i++){
  16. g[i].clear();
  17. cin>>a[i];
  18. }
  19. for(int i=1;i<=n;i++){
  20. if(!a[i]) g[i].emplace_back(mp(i,1));
  21. }
  22. for(int l=1;l<=n;l++){//init mex
  23. memset(ton, 0, sizeof(int) * (n + 5));
  24. int x=0;
  25. for(int r=l;r<=n;r++){
  26. ton[a[r]]++;
  27. while(ton[x]) x++;
  28. if(x>a[l] and a[r]<a[l]){
  29. g[r].push_back(mp(l,x));
  30. break;
  31. }
  32. }
  33. }
  34. for(int r=1;r<=n;r++){//init mex
  35. memset(ton, 0, sizeof(int) * (n + 5));
  36. int x=0;
  37. for(int l=r;l>=1;l--){
  38. ton[a[l]]++;
  39. while(ton[x]) x++;
  40. if(x>a[r] and a[l]<a[r]){
  41. g[r].push_back(mp(l,x));
  42. break;
  43. }
  44. }
  45. }
  46. memset(f[0],0,sizeof(bool)*((n<<1)+5));
  47. f[0][0]=1;
  48. for(int i=1;i<=n;i++){
  49. memcpy(f[i],f[i-1],sizeof(bool)*((n<<1)+5));
  50. for(pii j:g[i]){
  51. for(int k=0;k<=(n<<1);k++){
  52. if((k^j.second)<=(n<<1))f[i][k]|=f[j.first-1][k^j.second];
  53. }
  54. }
  55. // for(int j=0;j<=2*n;j++) cerr<<f[i][j]<<" ";
  56. // cerr<<endl;
  57. }
  58. for(int i=2*n;i>=0;i--){
  59. if(f[n][i]){cout<<i<<"\n";break;}
  60. }
  61. }
  62. }

CF1553G

观察到一个奇妙的性质:答案不会比

为什么,注意到从一个点生成的点一定和自己连边,同时偶数点之间都互相连边

欸,一个数乘自己加一,百分百偶数的

于是对两个点各生成一次 一定可以连通

所以答案 最大只有2

初始的连通性其实用一个并查集是可以维护的吧, 考虑筛一下质数然后对于所有质数枚举倍数

这一部分筛的复杂度是 ,枚举倍数大概 (其实应该略小一些吧) 然后合并应该不占复杂度或者占一个 ,其实真的是很健康的不是吗

欸我们枚举倍数的时候顺便把这个质数自己(哪怕不在给定中,不在就加点编号)塞到并查集里

这一部分咱大概当作

现在只需要考虑在初始不连的情况下,生成一个点能不能连就行嘻嘻

容易发现这个 是完全没用的,本来有的质因数不能连通更多的点的说

于是只需要考虑 的质因数会有什么后果——我们只需要把 的质因数中不同的考虑一下,而定位这些质因子所在的连通块是非常容易的因为塞进并查集了!

那么打标记即可,如果从 出发标了 所在的集合或者反过来那就答案为 ,再不行

所以至多也就 个的样子的质因数,然后枚举质因数set维护一下可达性

不能直接只改被查的两个集合

复杂度比较奇怪吧,感觉上界是 带一个有点大的常数

  1. #include<bits/stdc++.h>
  2. #define pii pair<int,int>
  3. #define mp make_pair
  4. using namespace std;
  5. const int N=2e6+5;
  6. const int A=1e6+1;
  7. bool vis[N];
  8. int prime[N],cnt;
  9. void init(){
  10. for(int i=2;i<=A;i++){
  11. if(!vis[i]){
  12. prime[++cnt]=i;
  13. }
  14. for(int j=1;j<=cnt and i*prime[j]<=A;j++){
  15. vis[i*prime[j]]=1;
  16. if(i%prime[j]==0){
  17. break;
  18. }
  19. }
  20. }
  21. }
  22. int n,q;
  23. int a[N];
  24. int rev[N];//back from val to num~
  25. int nb;
  26. int fa[N];
  27. int find(int x){
  28. return (x==fa[x]?x:fa[x]=find(fa[x]));
  29. }
  30. void merge(int x,int y){
  31. x=find(x),y=find(y);
  32. if(x==y) return;
  33. fa[x]=y;
  34. }
  35. vector<int> py[N];
  36. vector<pii > dui;
  37. set<pii> S;
  38. int main(){
  39. init();
  40. ios::sync_with_stdio(0);
  41. cin.tie(0),cout.tie(0);
  42. cin>>n>>q;
  43. nb=n;
  44. for(int i=1;i<=n;i++) cin>>a[i],rev[a[i]]=i,fa[i]=i;
  45. for(int t=1;t<=cnt;t++){
  46. int p=prime[t];
  47. if(!rev[p]){
  48. a[++nb]=p;
  49. rev[p]=nb;
  50. fa[nb]=nb;
  51. }
  52. for(int i=p;i<=A;i+=p){
  53. py[i].push_back(p);
  54. if(rev[i])merge(rev[p],rev[i]);
  55. }
  56. }
  57. for(int i=1;i<=n;i++){
  58. vector<int> cjb;
  59. cjb.clear();
  60. cjb.push_back(find(i));
  61. for(int v:py[a[i]+1]){
  62. cjb.push_back(find(rev[v]));
  63. }
  64. sort(cjb.begin(),cjb.end());
  65. cjb.erase(unique(cjb.begin(),cjb.end()),cjb.end());
  66. for(int i=0;i<cjb.size();i++){
  67. for(int j=i;j<cjb.size();j++){
  68. S.insert(mp(cjb[i],cjb[j]));
  69. }
  70. }
  71. }
  72. // for(pii v:S){
  73. // cerr<<v.first<<" "<<v.second<<endl;
  74. // }
  75. // cerr<<endl;
  76. for(int i=1;i<=q;i++){
  77. int s,t;
  78. cin>>s>>t;
  79. s=find(s),t=find(t);
  80. pii xzy=mp(min(s,t),max(s,t));
  81. // cerr<<find(s)<<" "<<find(t)<<endl;
  82. // cerr<<min(s,t)<<" "<<max(s,t)<<endl;
  83. if(s==t){
  84. cout<<"0\n";
  85. continue;
  86. }
  87. else if(S.count(xzy)){
  88. cout<<"1\n";
  89. }
  90. else cout<<"2\n";
  91. }
  92. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注