[关闭]
@suixinhita 2017-10-14T20:53:09.000000Z 字数 2757 阅读 995

郑外—衡中欢乐赛之 一

胡扯

先水水感想。
大概就是 noip之前的攒人品系列 因为...又是一次正解+细节挂掉。
看来我代码实现确实可以去好好搞一下...
根据我的水平,我感觉基本上t1 t2苟出来基本就是正常水平了,t3乱搞了个状压骗了10分....
当然我只苟住了t1...

题解

T1

此处输入图片的描述
图片挂了(。)
果然还是要搭建博客的(。)

手推可以得到,对于每一次的操作 都可以简化成 或者
所以总结一下就是

这不就是快速幂吗...
切之。

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<algorithm>
  4. #define ll long long
  5. using namespace std;
  6. ll n,m,k,Q;
  7. ll quick_pow(ll x,ll k)
  8. {
  9. ll an=1;
  10. for(int i=k;i;i>>=1,x=x*x%Q)
  11. if(i&1) an=an*x%Q;
  12. return an;
  13. }
  14. int main()
  15. {
  16. freopen("11.txt","r",stdin);
  17. freopen("1100.txt","w",stdout);
  18. cin>>n>>m>>k;
  19. Q=n+m;
  20. ll f=quick_pow(2,k);
  21. if(n>m) swap(n,m);
  22. n=n*f%Q;m=Q-n;
  23. if(n>m) swap(m,n);
  24. cout<<n;
  25. return 0;
  26. }

T2

这就是gg了的题,没错
此处输入图片的描述
首先我们可以发现 每一个对子 来说,我们可以将 减去 ,这样我们就能保证在 的时间里面求出所有合法的 了。(我们可以记录每一个数值的位置(注意这里是有重复的数字的,但是因为我们关心的是最近的在 后面的,所以直接倒着扫就可以解决)
然后我们对于所有求出来的位置求一个后缀最小值(以防当前 被后面某个 连累

强调!!!
当当前 时,需要寻找其后面最近的大于 的值。

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<algorithm>
  4. using namespace std;
  5. const int N=1e5+5;
  6. int a[N],end[N],pos[N],next[N],K,n;
  7. int find(int x)
  8. {
  9. int an=n+1;
  10. int k=a[x]-K;
  11. if(k==0) return next[x];
  12. if(k<0) return n+1;
  13. for(int i=1;i*i<=k;++i)
  14. {
  15. if(k%i==0)
  16. {
  17. int t=k/i;
  18. if(pos[i]&&i>K) an=min(an,pos[i]);
  19. if(pos[t]&&t>K) an=min(an,pos[t]);
  20. }
  21. }
  22. return an;
  23. }
  24. void solve()
  25. {
  26. long long ans=0;
  27. for(int i=n;i>=1;--i)
  28. {
  29. end[i]=find(i);
  30. pos[a[i]]=i;
  31. }
  32. long long minn=0x3f3f3f3f;
  33. for(int i=n;i>=1;--i)
  34. {
  35. minn=min((long long)end[i],minn);
  36. ans+=minn-i;
  37. }
  38. cout<<ans;
  39. }
  40. inline int read()
  41. {
  42. int x=0;char c=getchar();
  43. while(c<'0'||c>'9') c=getchar();
  44. while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
  45. return x;
  46. }
  47. int main()
  48. {
  49. // freopen("b.in","r",stdin);
  50. // freopen("110.txt","w",stdout);
  51. n=read(),K=read();
  52. for(int i=1;i<=n;++i) a[i]=read();
  53. for(int i=n;i>=1;--i)
  54. {
  55. if(a[i]>K)
  56. next[i]=i;
  57. else
  58. next[i]=next[i+1];
  59. }
  60. solve();
  61. return 0;
  62. }

T3

此处输入图片的描述

先感谢一下噗噗的图片(。)

这道题很要命。

首先 我们可以知道 状压50是非常可做的。
但是明显过不了。

所以我们考虑,不记录具体的状态,改为记录相对不那么具体的,比如位置个数和颜色数。
对于这道题来说,在dp的时候是有两个限制条件的,我们考虑两个分开处理(当然在状压的时候就已经用到这种考虑了)
我们可以先求一下,对于每一个数量的格子,确定颜色数的颜色集合个数。
然后再可以根据这个,假装我们没有上下不同的限制,选择先算后减,减去重复的。
没什么太大的具体实现难度,主要是能不能想得到不记录状态,只记录格子数和颜色数。

  1. #include<cstdio>
  2. #include<algorithm>
  3. #define ll long long
  4. using namespace std;
  5. const int N=1e6+5;
  6. const int M=5005;
  7. ll A[M],a[N],fac[N],sum[2];
  8. ll dp[2][N],f[M][M];//f[i][j]即为 第i行 染了j个的方案数
  9. int n,m,Q,now=0,last=1;
  10. void init()
  11. {
  12. A[0]=1;
  13. for(int i=1;i<=m;++i)
  14. A[i]= A[i-1]*(m-i+1)%Q;
  15. fac[0]=1;
  16. for(int i=1;i<M;++i)
  17. fac[i]= fac[i-1]*i%Q;
  18. f[0][0]=1;
  19. for(int i=1;i<M;++i)
  20. {
  21. for(int j=1;j<=i;++j)
  22. f[i][j]=( f[i-1][j]*(j-1)%Q+f[i-1][j-1])%Q;
  23. }
  24. }
  25. void solve()
  26. {
  27. dp[0][0]=1;sum[0]=1;
  28. for(int i=1;i<=n;++i)
  29. {
  30. swap(now,last);
  31. sum[now]=0;
  32. for(int j=1;j<=a[i];++j)
  33. {
  34. dp[now][j]= A[j]*f[a[i]][j]%Q*sum[last]%Q;//假装不用管上下限制
  35. if(j<=a[i-1])
  36. dp[now][j]=(dp[now][j]- fac[j]*f[a[i]][j]%Q*dp[last][j]%Q+Q)%Q;
  37. //然后我们发现我们需要减去上下限制,这时候我们减去一个上一个重复的 fac代表排列
  38. sum[now]=( sum[now]+dp[now][j])%Q;
  39. }
  40. }
  41. printf("%lld",sum[now]);
  42. }
  43. int main()
  44. {
  45. scanf("%d%d%d",&n,&m,&Q);
  46. for(int i=1;i<=n;++i) scanf("%d",&a[i]);
  47. init();
  48. solve();
  49. return 0;
  50. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注