[关闭]
@rebirth1120 2019-10-25T21:26:44.000000Z 字数 5135 阅读 505

2019,10,25 长郡 csp

未分类


不得不说一句, 这位出题人的文笔还真不错...

T1 忧郁 gloomy

  学校里的樱花开了又谢,小W 向窗外望去,只有光秃秃的枝桠在秋风中挺立。他的离
开,似乎也带走了她的春天,她的心里没有了粉红色的花海,没有了和煦的春风,只剩下光秃秃的枝桠,和一颗无处安放的心。
  “该走了。”小W 晃晃脑袋,披上外套,打开前门。即使是有所准备,迎面的秋风还是打了她一个措手不及。小W缩紧了身子,不过那熟悉的怀抱并没有来到。小W 愣了一下,突然笑了,“今年的秋天,还真是冷呢。”
  去图书馆的路上已经堆满了银杏叶,树上的叶在呼啸的秋风中飘零。小W看着空中的树叶,仿佛看到了樱花树上粉红的花片片凋零的样子。“是不是越美的事物越容易消逝呢?如果心中的忧郁也能像樱花一样飘落,就好了。”小W心中的忧郁又涌上心头。“忧郁似乎也像微笑一样能够传递,最近也让大家或多或少地忧郁起来了呢,这些东西还是回到我身上来比较好吧。”低语中,小W已经进入图书馆研究同学之间分担忧郁的关系网络了。

题意

个学生 , 它们之间有 条忧郁值分担关系 ,

一条关系 表示 分担 单位的忧郁值 ,
,

现在, 小W 可以执行若干次命令 , 表示让 分担 单位 的忧郁值,
,

问小W 至少要执行多少次命令才能使所有人的忧郁值归零 (初始时忧郁值为零).

方法

这道题的描述有点迷...差点就没看懂题... (单押*1)

若有 的点的权值总和为 0, 则可以执行 次命令使它们的权值归零,
要是执行命令的次数最少, 我们就要把这 个点分成尽量多个权值和为 0 的部分.

考场上写了个复杂度似乎很正确的爆搜, 结果 20pts...
复杂度确实正确, WA了...
因为每次把能凑在一起的点凑一起不一定是最优方案,

正解是状压dp, 设 为点集为 时, 权值和为 0 的块数的最大值,
再记一个 , 表示权值和, 每次枚举一个新点转移就行了, 方程如下

后面的那个中括号是个 bool 变量

代码

(加了一些奇怪的卡常优化...)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=24+7;
  4. const int M=16777216+7;
  5. int T,n,m,val[N],bi[N],f[M],sum[M],q0[N],q1[N],fl[M],num[M];
  6. void print(int x){
  7. for(int i=1;i<=n;i++){ printf("%d",x&1); x>>=1; }
  8. }
  9. int main(){
  10. // freopen("gloomy.in","r",stdin);
  11. // freopen("gloomy.out","w",stdout);
  12. cin>>n>>m;
  13. int x,y,w;
  14. for(int i=1;i<=m;i++){
  15. scanf("%d%d%d",&x,&y,&w);
  16. val[x]+=w; val[y]-=w;
  17. }
  18. for(int i=0;i<n;i++){
  19. if(val[i]<0){ q0[++q0[0]]=i; if(val[i]<0) fl[bi[i]]=-1; }
  20. else{ q1[++q1[0]]=i; if(val[i]>0) fl[bi[i]]=1; }
  21. bi[i]=1<<i;
  22. num[bi[i]]=i;
  23. }
  24. int all=(1<<n)-1;
  25. int cnt=0;
  26. for(int i=0;i<=all;i++){
  27. int j=num[i&(-i)];
  28. if(i){
  29. sum[i]=sum[i^bi[j]]+val[j];
  30. if(fl[i^bi[j]]==-1&&val[j]<=0){ fl[i]=-1; continue; }
  31. else if(fl[i^bi[j]]==1&&val[j]>=0){ fl[i]=1; continue; }
  32. }
  33. int lim=sum[i]>0 ?q0[0] :q1[0];
  34. for(int k=1;k<=lim;k++){
  35. int j=sum[i]>0 ?q0[k] :q1[k];
  36. if(!(i&bi[j])){
  37. if(sum[i]+val[j]==0) f[i|bi[j]]=max(f[i|bi[j]],f[i]+1);
  38. else f[i|bi[j]]=max(f[i|bi[j]],f[i]);
  39. }
  40. }
  41. }
  42. printf("%d\n",n-f[all]);
  43. return 0;
  44. }

T2 枣子 jujube

  天色渐晚,暮色像一头小熊,笨拙、漂亮地攀爬着天空的梯子。在落日余晖的照映下,小W 家门口的枣树披上了金色的衣,树上的枣子红灿灿的,甚是可人。仔细想来,枣树也已〸六有余,恰与她同岁呢。小时候小W就认为,门口的那株枣树就是她的守护神。无论春夏还是秋冬,都陪在她的身旁。天热了,洒下一片阴凉,她和朋友们打闹嬉戏;天凉了,结出一颗颗枣子,甜甜的,沁人心脾,她和家人在树下吃枣闲聊。似乎所有的美妙回忆都离不开这株枣树,所有的伤心故事也在枣树下消散殆尽。

题意

给定三个数 , 求 能表示为多少种 的形式 .

方法

为化简后的 , 考场上只考虑的了 同底数的情况, 没有考虑把 表示为 的情况, 所以...

那么还是先处理出 , 表示 能表示为多少种 "枣子塔" 的形式 (没错就是上面那货).

然后把 化简为 , 然后质因数分解 (先分解 , 然后再分解 ).

那么每一个质因子的每一个次方都可以放在下面或留在上面, 变成 ( 就是放在下面的).

那么对于每一个 , 质因子 的贡献就是 ( 种留在上面的情况再加上一种放在下面的情况),
最终的答案就是 (减去全部都放在下面的情况).

代码

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. using namespace std;
  4. const int N=7e5+7;
  5. const ll mod=998244353;
  6. int T,a,b,c,top,num[N],v[N],pri[N],cnt;
  7. ll f[N],g[N],r[N],ans,maxr;
  8. void pre(){
  9. for(int i=2;i<=20;i++){
  10. f[i]=1;
  11. for(int j=2;j*j<=i;j++)
  12. if(!(i%j)){
  13. int t=i,cnt=0;
  14. while(!(t%j)){ t/=j; cnt++; }
  15. if(t==1) f[i]+=f[cnt];
  16. }
  17. }
  18. for(ll i=2;i<=640000;i++){
  19. if(i>20) f[i]++;
  20. ll t=i*i;
  21. for(int j=2;t<=640000&&t>0;j++,t*=i)
  22. if(t>20) f[t]+=f[j];
  23. }
  24. for(int i=2;i<=200;i++){
  25. if(!v[i]){ v[i]=i; pri[++cnt]=i; }
  26. for(int j=1;j<=cnt;j++){
  27. if(pri[j]>v[i]||pri[j]*i>200) break;
  28. v[pri[j]*i]=pri[j];
  29. }
  30. }
  31. }
  32. int simp(int &x){
  33. for(int i=2;i*i<=x;i++)
  34. if(!(x%i)){
  35. int t=x,cnt=0;
  36. while(!(t%i)){ t/=i; cnt++; }
  37. if(t==1){ x=i; return cnt; }
  38. }
  39. return 1;
  40. }
  41. void divd(int x,int tms){
  42. for(int i=1;pri[i]*pri[i]<=x;i++)
  43. if(!(x%pri[i])){
  44. int cnt=0;
  45. while(!(x%pri[i])){ x/=pri[i]; cnt++; }
  46. if(!num[pri[i]]) num[pri[i]]=++top;
  47. r[num[pri[i]]]+=cnt*tms;
  48. maxr=max(maxr,r[num[pri[i]]]);
  49. }
  50. if(x!=1){
  51. if(!num[x]) num[x]=++top;
  52. r[num[x]]+=tms;
  53. maxr=max(maxr,r[num[x]]);
  54. }
  55. }
  56. int main(){
  57. // freopen("jujube.in","r",stdin);
  58. pre();
  59. cin>>T;
  60. while(T--){
  61. memset(r,0,sizeof(r));
  62. memset(num,0,sizeof(num));
  63. top=0;
  64. scanf("%d%d%d",&a,&b,&c);
  65. int k=simp(a);
  66. divd(k,1);
  67. divd(b,c);
  68. ans=0;
  69. for(ll rr=2;rr<=maxr;rr++){
  70. ll res=1;
  71. for(int i=1;i<=top;i++) res=res*(r[i]/rr+1)%mod;
  72. ans=(ans+(res-1)*f[rr]%mod)%mod;
  73. }
  74. printf("%lld\n",ans);
  75. }
  76. return 0;
  77. }

T3 开关 switch

  夜已深了,银白色的月光洒在地上,香樟叶被风抚过,树影婆娑。树旁教学楼的灯一盏盏熄灭,与树相伴的只剩下小W。教室里奋笔疾书的沙沙声在如刀入鞘般的合笔声中结束,小W 轻呼出一口气。该走了,她这样想着。

题意

盏灯 , 其中前 盏是开着的 ,

在每次操作中, 小W 每次可以选择任意盏灯, 将它们的状态取反, 若干次操作构成一个方案.

一个方案是合法的, 当且仅当:
1. 这个方案是一个集合 (操作不重复).
2. 执行完这个方案后, 所有灯都熄灭了.

求有多少种大小为 的方案
(方案大小的为它的操作个数).

做法

考场上大概还剩半个小时才看这道题, 本来想就写个暴力, 结果不会写...
然后随便推了个式子, 样例都没过...
最后特判了几种情况, 20pts...

题目要求的是集合 (也就是 ), 为了方便, 我们先认为它是有顺序的 (因为 dp 的话需要一个拓扑序), 最后再除以一个 就行了,

考虑设 为当 时的方案数, 若不考虑重复, 那么 (因为最后一位是固定的).
由于前面选的 个操作都不会重复, 不合法的情况就只有当前需要选的那个操作在之前已经被选过了,

那我们把所有不合法的情况按照重复的操作进行分类, 最后加法原理就行了,

考虑若第 次操作与前面一个操作重复了, 那么这两个操作就相当于抵消了, 剩下的 个操作的异或和就应该要能把全部灯熄灭, 情况数就是 , 然后对于每一个重复的操作 (每一类不合法的情况), 我们还要枚举它出现在哪一位, 所以不合法的情况为


其中 是枚举每一个重复的操作,
是重复操作的位置,

所以转移方程为

代码

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. using namespace std;
  4. const int M=1e3+7;
  5. const ll mod=19260817;
  6. int T;
  7. ll n,m,v;
  8. ll f[M];
  9. ll q_pow(ll a,ll p){
  10. ll res=1;
  11. while(p){
  12. if(p&1) res=res*a%mod;
  13. p>>=1;
  14. a=a*a%mod;
  15. }
  16. return res;
  17. }
  18. ll inv(ll x){ return q_pow(x,mod-2); }
  19. ll dif(ll x,ll y){ return ((x-y)%mod+mod)%mod; }
  20. int main(){
  21. // freopen("switch.in","r",stdin);
  22. cin>>T;
  23. while(T--){
  24. cin>>n>>m>>v;
  25. f[0]=v==0; f[1]=1;
  26. ll tot=q_pow(2,n),res=1,fac=1;
  27. for(ll i=2;i<=m;i++){
  28. res=res*dif(tot,i-2)%mod;
  29. fac=fac*i%mod;
  30. f[i]=dif(res%mod,dif(tot,i-2)*(i-1)%mod*f[i-2]%mod);
  31. }
  32. printf("%lld\n",f[m]*inv(fac)%mod);
  33. }
  34. return 0;
  35. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注