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

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. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注