【LGR-054】洛谷10月月赛II

【LGR-054】洛谷10月月赛II

luogu

成功咕掉Codeforces Round #517的后果就是,我\(\mbox{T4}\)依旧没有写出来。\(\mbox{GG}\)

浏览器

\(\mbox{popcount}\)\(0\)的乘上\(\mbox{popcount}\)\(1\)的就是答案。

因为两个数异或以后二进制位\(1\)的个数的奇偶性不会变。

至于计算\(\mbox{popcount}\),预处理到根号,\(O(1)\)计算即可。

#include<cstdio>
#include<algorithm>
using namespace std;
int gi(){
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
#define ll long long
int n,cnt[65536],sz[2];ll a,b,c,d,x;
int main(){
	n=gi();a=gi();b=gi();c=gi();d=gi();x=gi();
	for (int i=0;i<65536;++i) cnt[i]=cnt[i>>1]^(i&1);
	for (int i=1;i<=n;++i){
		x=(a*x%d*x%d+b*x%d+c)%d;
		++sz[cnt[x&65535]^cnt[x>>16]];
	}
	printf("%lld\n",1ll*sz[0]*sz[1]);
	return 0;
}

大师

直接枚举公差,然后\(O(n)\)扫一遍即可。

#include<cstdio>
#include<algorithm>
using namespace std;
int gi(){
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 20005;
const int mod = 998244353;
void inc(int &x,int y){x+=y;x>=mod?x-=mod:x;}
int n,a[N],f[N],s[N],ans;
int main(){
	n=gi();ans=n;
	for (int i=1;i<=n;++i) a[i]=gi();
	for (int i=-20000;i<=20000;++i){
		for (int j=1;j<=n;++j){
			if (a[j]-i>=0&&a[j]-i<=20000) f[j]=s[a[j]-i]+1;
			else f[j]=1;
			inc(s[a[j]],f[j]);inc(ans,f[j]);
		}
		inc(ans,mod-n);
		for (int j=1;j<=n;++j) s[a[j]]=0;
	}
	printf("%d\n",ans);return 0;
}

礼物

限制条件是如果一个数是另一个数的子集,那么两个数不能被放在同一个盒子里。

对关系建一个\(DAG\),每个数向自己的所有子集连边,这样拥有拓扑关系的两个数就不能放在一起。答案就是\(DAG\)最长链长度。

构造方案的话,建立一个源点跑出到每个点\(i\)的最长路\(f_i\),那么\(i\)就放到编号为\(f_i\)的盒子里就行了。

暴力建边\(3^k\),注意当\(n=2^k\)\(a_i\)取遍\([0,2^k)\)时,建出这个\(DAG\)只需要连\(O(k2^k)\)条边就行了。如果不满的话,还是把这\(2^k\)个点都建出来,不存在的点权值设为\(0\),依然按上述做法做即可。

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
int gi(){
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 2e6+5;
int n,k,S,vis[N],f[N];vector<int>ans[21];
int main(){
	n=gi();k=gi();S=(1<<k)-1;
	for (int i=1;i<=n;++i) vis[gi()]=1;
	for (int i=S;~i;--i){
		if (vis[i]) ++f[i];
		for (int j=1;j<S;j<<=1)
			if (i&j) f[i^j]=max(f[i^j],f[i]);
	}
	printf("1\n%d\n",f[0]);
	for (int i=S;~i;--i) if (vis[i]) ans[f[i]].push_back(i);
	for (int i=1;i<=f[0];++i){
		int sz=ans[i].size();printf("%d ",sz);
		for (int j=0;j<sz;++j) printf("%d ",ans[i][j]);
		puts("");
	}
	return 0;
}

口袋里的纸飞机

考虑计算每个数\(x\)会在多少种数列中被生成出来。进一步的,因为不能确定生成的\(x\)的个数,所以考虑计算\(x\)在多少数列中不会被生成出来。

对数列中的每个数对\(P\)取模,这样每个\([0,P)\)就可以有\(\lfloor\frac RP\rfloor\)\(\lfloor\frac RP\rfloor+1\)种不同选法。

如果\(x=0\),那么只要满足数列中不存在\(0\)就可以了。

如果\(x\neq0\),那么对于每个\(a\in[1,P)\),都有一个唯一且互不相同的\(b\in[1,P)\)使得\(a\times b=x\mod p\)。这样相当于是对于一个\(x\neq0\),存在若干对无交集的\((a,b)\),要求同一对中的两个数不能同时出现。

这个东西的方案数怎么计算呢?你发现\(n\le500\),也许是......生成函数?

说一种显得不那么劝退的做法。考虑设\(f_{i,j}\)表示用了前\(i\)对数填了\(j\)个位置的方案数,转移显然就是枚举当前这对数填多少个,同时因为位置可以任意选所以还得要乘上一个组合数。这样一写出来就会发现他就是一个指数生成函数卷积的形式,即\(C_i=\sum_{j=0}^i\binom ijA_jB_{i,j}\)

所以对于每个\(x\)大有概\(O(P)\)的数对,每个数对可以用一个指数生成函数表示,把这\(O(P)\)个生成函数暴力卷起来,就可以得到一个\(O(n^2P^2)\)的做法。

然后因为每个\([0,P)\)只有\(\lfloor\frac RP\rfloor\)\(\lfloor\frac RP\rfloor+1\)种不同的选法,所以本质不同的数对只有\(3\)种。我们考虑一下每种数对的生成函数。令\(B=\lfloor\frac RP\rfloor\)

数对中两个数都只有\(B\)种选法,考虑计算无限制减去两个数同时出现的方案:\(A(x)=e^{Bx}e^{Bx}-(e^{Bx}-1)e^{Bx}-1)=2e^{Bx}-1\)。其中\(e^{Bx}\)实际上就是\((e^x)^B\)

同理有\(B(x)=e^{Bx}+e^{(B+1)x}-1,C(x)=2e^{(B+1)x}-1\)

假设对于某个\(x\)它三种数对的个数分别是\(u,v,w\),那么我们就只需要计算\(A^u(x)\times B^v(x)\times C^w(x)\times e^{Bx}\)就行了。

为什么还有一个\(e^{Bx}\)?因为数列中还可以有\(0\)呀。这一点你有没有注意到呢?

所以我们先预处理出\(A(x),B(x),C(x)\)\(P\)次幂,这样预处理的复杂度可以做到\(O(n^P)\),然后对于每个\(x\)都可以\(O(n^2)\)地计算卷积,总复杂度还是\(O(n^2P)\),可以获得\(80\)分的好成绩。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int mod = 1000000007;
inline void inc(int &x,int y){x+=y;x>=mod?x-=mod:x;}
inline void dec(int &x,int y){x-=y;x<0?x+=mod:x;}
int fastpow(int a,int b){
    int res=1;
    while (b) {if (b&1) res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}
    return res;
}
int n,P,R,B,S,inv[5005],C[505][505],ans;
struct poly{
    int a[505];
    poly(){memset(a,0,sizeof(a));}
    poly operator * (poly b){
        poly c;
        for (int i=0;i<=n;++i)
            for (int j=0;j<=i;++j)
                c.a[i]=(c.a[i]+1ll*a[j]*b.a[i-j]%mod*C[i][j])%mod;
        return c;
    }
}dp[3][5005],zero;
int main(){
    n=gi();P=gi();R=gi();B=R/P;
    inv[1]=1;
    for (int i=2;i<P;++i) inv[i]=inv[P%i]*(P-P/i)%P;
    for (int i=C[0][0]=1;i<=n;++i)
        for (int j=C[i][0]=1;j<=i;++j)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    for (int i=0;i<3;++i){
        dp[i][0].a[0]=dp[i][1].a[0]=1;
        for (int j=1;j<=n;++j) dp[i][1].a[j]=(fastpow(B+(i>0),j)+fastpow(B+(i>1),j))%mod;
        for (int j=2;j<=P;++j) dp[i][j]=dp[i][j-1]*dp[i][1];
    }
    for (int i=0;i<=n;++i) zero.a[i]=fastpow(B,i);
    S=fastpow(R,n);inc(ans,S);dec(ans,fastpow(R-B,n));
    for (int i=1;i<P;++i){
        int u=0,v=0,w=0;
        for (int j=1;j<P;++j){
            int x=inv[j]*i%P;
            if (x<j){
                if (j<=R-B*P) ++w;
                else if (x<=R-B*P) ++v;	
                else ++u;
            }
        }
        poly res=dp[0][u]*dp[1][v]*dp[2][w]*zero;
        inc(ans,S);dec(ans,res.a[n]);
    }
    printf("%d\n",ans);
    return 0;
}

满分做法比较玄学。

首先,要求\(A(x)\)的若干次幂其实只要预处理到根号就行了,即预处理\(A(x),A^2(x),A^3(x)...\)以及\(A^{\sqrt p}(x),A^{2\sqrt p}(x),A^{3\sqrt p}(x)...\)这样预处理的复杂度可以降到\(O(n^2\sqrt P)\),且对于一个\(x\)仍可以做到\(O(n^2)\)计算。

然后后本部分仍是复杂度瓶颈?发现对于每个\(x\)答案只与三种数对的个数\(u,v,w\)有关,所以加个记忆化......就能过了?

司说本质不同的数对数量是\(O(\sqrt P)\)的,那......就是吧。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
int gi(){
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int mod = 1000000007;
inline void inc(int &x,int y){x+=y;x>=mod?x-=mod:x;}
inline void dec(int &x,int y){x-=y;x<0?x+=mod:x;}
int fastpow(int a,int b){
	int res=1;
	while (b) {if (b&1) res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}
	return res;
}
int n,P,R,B,sqr,S,inv[5005],C[505][505],ans;
struct poly{
	int a[505];
	poly(){memset(a,0,sizeof(a));}
	poly operator * (poly b){
		poly c;
		for (int i=0;i<=n;++i)
			for (int j=0;j<=i;++j)
				c.a[i]=(c.a[i]+1ll*a[j]*b.a[i-j]%mod*C[i][j])%mod;
		return c;
	}
}dp[3][2][80],zero;
map<pair<pair<int,int>,int>,int>M;
int calc(int u,int v,int w){
	return (dp[0][0][u%sqr]*dp[0][1][u/sqr]*dp[1][0][v%sqr]*dp[1][1][v/sqr]*dp[2][0][w%sqr]*dp[2][1][w/sqr]*zero).a[n];
}
int main(){
	n=gi();P=gi();R=gi();B=R/P;while (sqr*sqr<P) ++sqr;
	inv[1]=1;for (int i=2;i<P;++i) inv[i]=inv[P%i]*(P-P/i)%P;
	for (int i=C[0][0]=1;i<=n;++i)
		for (int j=C[i][0]=1;j<=i;++j)
			C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
	for (int i=0;i<3;++i){
		dp[i][0][0].a[0]=dp[i][1][0].a[0]=dp[i][0][1].a[0]=1;
		for (int j=1;j<=n;++j) dp[i][0][1].a[j]=(fastpow(B+(i>0),j)+fastpow(B+(i>1),j))%mod;
		for (int j=2;j<=sqr;++j) dp[i][0][j]=dp[i][0][j-1]*dp[i][0][1];
		for (int j=1;j<=sqr;++j) dp[i][1][j]=dp[i][1][j-1]*dp[i][0][sqr];
	}
	for (int i=0;i<=n;++i) zero.a[i]=fastpow(B,i);
	S=fastpow(R,n);inc(ans,S);dec(ans,fastpow(R-B,n));
	for (int i=1;i<P;++i){
		int u=0,v=0,w=0;
		for (int j=1;j<P;++j){
			int x=inv[j]*i%P;
			if (x<j){
				if (j<=R-B*P) ++w;
				else if (x<=R-B*P) ++v;
				else ++u;
			}
		}
		pair<pair<int,int>,int>pr=make_pair(make_pair(u,v),w);
		if (M.find(pr)==M.end()) M[pr]=calc(u,v,w);
		inc(ans,S);dec(ans,M[pr]);
	}
	printf("%d\n",ans);return 0;
}
posted @ 2018-10-22 14:59  租酥雨  阅读(544)  评论(0编辑  收藏  举报