NOI Online #3 提高组

水壶

题意:

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

\(m+1\leq n\leq 10^6\)

题解:

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

#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\)天为\(f_{i,0}\),之后每天,节点\(v\)的权值变化为前一天所有和它直接相连节点的异或和,问\(q\)次,问第\(k\)\(1\)号节点的权值。

\(n,q\leq 100,m\leq 10000,f_{i,0},k< 2^{32}\)

题解:

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

我们把有边直接相连的两个点\(x,y\)之间的邻接矩阵\(G\)值记作\(2^{32}-1\),其它值记作\(0\)

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

\[f[i][k]=\sum f[j][k-1]\&G[i][j] \]

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

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

其实和上面的式子类似

\[G[x][z][k]=\sum G[x][y][k-1]\&G[y][z][k-1] \]

\(\sum\)同样是异或和。

这样就可以矩阵快速幂了,复杂度\(O(n^3*log*q)\),小慢。

转移答案矩阵只要\(O(n^2)\),而转移邻接矩阵要\(O(n^3)\)

二进制拆分,先预处理出所有二的幂次的转移矩阵\(O(n^3log)\)

询问的时候再做快速幂,\(O(n^2qlog)\)

#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\)。一个子序列的贡献是\(\varphi(1+\sum a_i)\)

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

\(n\leq 10^6,a_i\leq 2*10^5\)

题解:

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

\(dp[S]\)是优秀子序列的二进制和是\(S\)的方案数,明显可以枚举子集做到\(3^{18}\)

特别注意\(dp[0]=2^{c[0]}\)

然后就过了,呃

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

#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 @ 2022-05-13 11:07  lovelyred  阅读(33)  评论(0编辑  收藏  举报