NOI Online #3 提高组

水壶#

题意:

给一排n个水壶,有m次操作,每次可以选一个壶,把里面的水倒入右边一个壶,最后把一个壶里的水喝掉,最多能喝多少水?

m+1n106

题解:

选出和最大的连续的m+1个,前缀和。

Copy
#include<bits/stdc++.h> using namespace std; namespace red{ #define int long long #define ls(p) (p<<1) #define rs(p) (p<<1|1) #define mid ((l+r)>>1) #define lowbit(i) ((i)&(-i)) const int N=1e6+10; int n,m,ans; int a[N],s[N]; inline void main() { ios::sync_with_stdio(0); cin.tie(0),cout.tie(0); cin>>n>>m; ++m; for(int i=1;i<=n;++i) { cin>>a[i]; s[i]=s[i-1]+a[i]; } for(int i=m;i<=n;++i) { ans=max(ans,s[i]-s[i-m]); } cout<<ans<<'\n'; } } signed main() { red::main(); return 0; } /* */

魔法值#

题意:

n个点,m条边,每个点上有点权,第0天为fi,0,之后每天,节点v的权值变化为前一天所有和它直接相连节点的异或和,问q次,问第k1号节点的权值。

n,q100,m10000,fi,0,k<232

题解:

变化可以看作一个矩阵的形式

我们把有边直接相连的两个点x,y之间的邻接矩阵G值记作2321,其它值记作0

那么每次一个节点的权值变化可以写作:

f[i][k]=f[j][k1]&G[i][j]

其中在这里是异或和,f[i][k]是第k天,i号节点的值。

那考虑后一天的邻接矩阵值怎么由前一天得到

其实和上面的式子类似

G[x][z][k]=G[x][y][k1]&G[y][z][k1]

同样是异或和。

这样就可以矩阵快速幂了,复杂度O(n3logq),小慢。

转移答案矩阵只要O(n2),而转移邻接矩阵要O(n3)

二进制拆分,先预处理出所有二的幂次的转移矩阵O(n3log)

询问的时候再做快速幂,O(n2qlog)

Copy
#include<bits/stdc++.h> using namespace std; namespace red{ #define int long long #define ls(p) (p<<1) #define rs(p) (p<<1|1) #define mid ((l+r)>>1) #define lowbit(i) ((i)&(-i)) const int N=100+10; int n,m,k; int a[N]; int f[N][N]; struct matrix { int g[N][N]; }T[32],ans; inline void mul(matrix &ans,matrix T,int n,int m) { matrix tmp; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) tmp.g[i][j]=0; for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { for(int k=1;k<=m;++k) { tmp.g[i][j]^=ans.g[i][k]&T.g[k][j]; } } } for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) ans.g[i][j]=tmp.g[i][j]; } inline void fast() { for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) T[0].g[i][j]=f[i][j]; for(int k=1;k<=31;++k) { for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) T[k].g[i][j]=T[k-1].g[i][j]; mul(T[k],T[k-1],n,n); } } inline void main() { ios::sync_with_stdio(0); cin.tie(0),cout.tie(0); cin>>n>>m>>k; for(int i=1;i<=n;++i) cin>>a[i]; for(int i=1;i<=m;++i) { int x,y;cin>>x>>y; f[x][y]=f[y][x]=(1ll<<32)-1; } fast(); for(int i=1;i<=k;++i) { int x;cin>>x; for(int i=1;i<=n;++i) ans.g[1][i]=a[i]; for(int k=31;k>=0;--k) if((x>>k)&1) { mul(ans,T[k],1,n); } cout<<ans.g[1][1]<<'\n'; } } } signed main() { red::main(); return 0; } /* */

优秀子序列#

题意:

给定序列A,一个优秀子序列是指,这个子序列任意两个数的与为0。一个子序列的贡献是φ(1+ai)

问所有优秀子序列的贡献。

n106,ai2105

题解:

一个优秀子序列,二进制下每一位肯定只出现一次。

dp[S]是优秀子序列的二进制和是S的方案数,明显可以枚举子集做到318

特别注意dp[0]=2c[0]

然后就过了,呃

还有更优秀的子集卷积做法,不会

Copy
#include<bits/stdc++.h> using namespace std; namespace red{ #define int long long #define ls(p) (p<<1) #define rs(p) (p<<1|1) #define mid ((l+r)>>1) #define lowbit(i) ((i)&(-i)) const int N=1e6+10,mod=1e9+7; int n,ans; int c[N],dp[N]; inline int phi(int x) { //cout<<x<<"!!"<<endl; int ret=x; for(int i=2;i*i<=x;++i) { if(x%i==0) { ret=ret*(i-1)/i; while(x%i==0) x/=i; } } if(x>1) ret=ret*(x-1)/x; //cout<<ret<<"!!!!"<<endl; return ret; } inline void main() { ios::sync_with_stdio(0); cin.tie(0),cout.tie(0); cin>>n; for(int i=1;i<=n;++i) { int x;cin>>x; ++c[x]; } dp[0]=1; for(int i=1;i<=c[0];++i) dp[0]=dp[0]*2%mod; for(int s=0;s<(1<<18);++s) { for(int sub=s;sub;sub=(sub-1)&s) { if(sub<(s^sub)) break; dp[s]=(dp[s]+(c[sub]*dp[s^sub]))%mod; } if(dp[s]) { //cout<<s<<' '<<dp[s]<<"!"<<endl; ans=(ans+dp[s]*phi(1+s))%mod; } } cout<<ans<<'\n'; } } signed main() { red::main(); return 0; } /* 4 0 2 2 3 */
posted @   lovelyred  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示
CONTENTS