ZJOI2017Day1题解(真·抄标解)

BZOJ4784-4786

(直接进正题)

T1:

(敢不敢再短点)

step1:如果不是仙人掌,那显然是没有救的。。(puts("0");)

step2:如果是,先拆开环。你会发现每棵树之间的方案数是可以用乘法定理的,然后就变成了求树的问题。。

step3:(然后据说树形dp+FFT可以过,然而完全不用这么做)把剩下的非环边钦定为重边,然后你发现每个点都必须至少向外连一条边

step4:这样之后考虑每个点为根时几个儿子两两匹配的方案数就好了

dp[i]表示i个东西两两匹配的方案数。考虑递推算,dp[i]=(i-1)*dp[i-2]+dp[i-1](要么加一个点不连,要么与其中一个点连边,其余的随意(dp[i-2]))

#include 
#define gc getchar()
#define ll long long
#define mod 998244353
#define N 500009
#define M N<<1
using namespace std;
ll n,m,number,dfn[N],cnt,dp[N],fa[N],vis[N],first[N],size[N];
struct edge
{
	ll to,next;
	void add(ll x,ll y)
	{
		to=y,next=first[x],first[x]=number;
	}
}e[M<<1];
ll read()
{
	ll x=1;
	char ch;
	while (ch=gc,ch<'0'||ch>'9') if (ch=='-') x=-1;
	ll s=ch-'0';
	while (ch=gc,ch>='0'&&ch<='9') s=s*10+ch-'0';
	return s*x;
}
ll dfs(ll x)
{
	dfn[x]=++cnt;
	for (ll i=first[x];i;i=e[i].next)
		if (!dfn[e[i].to])
		{
			fa[e[i].to]=x;
			if (dfs(e[i].to)) return 1;
		}
		else
			if (dfn[e[i].to]>dfn[x])
			{
				size[x]-=2;
				for (ll y=e[i].to;y!=x;y=fa[y])
					if (vis[y]) return 1;
					else vis[y]=1,size[y]-=2;	
			}
	return 0;
}
int main()
{
	ll T=read();
	dp[0]=dp[1]=1;
	for (ll i=2;i
参考 qzh_1430586275

T2:

(概率吓得根本不敢做。。)

我们可以轻(jian)易(nan)发现,这个错误树状数组的qry就是在求后缀异或和,然后与答案相等其实就是a[l-1]=a[r](这是原数组)

当然还要特判(似乎很坑啊),当l=1时,qry(l-1)=0,然后就是r的前缀异或和=r的后缀异或和,也就是a[r]=sigma (i=1~n) a[i],这个特判显然开个线段树区间维护,单点查询就好了。

然后回到一般情况,考虑用二维线段树维护一个东西:就是两维的坐标的代表的编号(x,y),a[x]=a[y]的概率。然后考虑插入时,只需要考虑上半部分,因为qry时r显然要大于l。

考虑合并两个概率,设p1为原来相同的概率,p2为加入的相同的概率,那答案就是p=p1*p2+(1-p1)*(1-p2)。(逆元搞一搞其实提示的似乎并不需要,预处理显然更快啊)

#include 
#define gc getchar()
#define ll long long
#define mod 998244353
#define N 100009
#define mid ((l+r)>>1)
#define Root rt[cur],1,n
#define Lson ls[cur],l,mid
#define Rson rs[cur],mid+1,r
#define root 1,1,n
#define lc cur<<1
#define rc lc|1
#define lson lc,l,mid
#define rson rc,mid+1,r
#define now cur,l,r
using namespace std;
int n,m,x,y,z,inv[N],rt[N<<2],ls[N<<8],rs[N<<8],cnt,sum;
struct pro
{
	int p;
	pro(int p=0):p(p){}
}s[N<<8],ans,tg[N<<2];
pro operator +(const pro &a,const pro &b)
{
	return pro(((ll)a.p*b.p%mod+(ll)(1-a.p+mod)*(1-b.p+mod)%mod)%mod);
}
int read()
{
	int x=1;
	char ch;
	while (ch=gc,ch<'0'||ch>'9') if (ch=='-') x=-1;
	int s=ch-'0';
	while (ch=gc,ch>='0'&&ch<='9') s=s*10+ch-'0';
	return s*x;
}
void ins2(int &cur,int l,int r,int L,int R,pro p)
{
	if (!cur) s[cur=++cnt]=pro(1);
	if (L<=l&&R>=r)
	{
		s[cur]=s[cur]+p;
		return;
	}
	if (L<=mid) ins2(Lson,L,R,p);
	if (R>mid) ins2(Rson,L,R,p);
}
void ins1(int cur,int l,int r,int L1,int R1,int L2,int R2,pro p)
{
	if (L1<=l&&R1>=r)
	{
		ins2(Root,L2,R2,p);
		return;
	}
	if (L1<=mid) ins1(lson,L1,R1,L2,R2,p);
	if (R1>mid) ins1(rson,L1,R1,L2,R2,p);
}
pro qry2(int cur,int l,int r,int x)
{
	if (!cur) return pro(1);
	if (l==r) return s[cur];
	if (x<=mid) return s[cur]+qry2(Lson,x);
	else return s[cur]+qry2(Rson,x);
}
pro qry1(int cur,int l,int r,int x,int y)
{
	pro tmp=pro(1);
	if (rt[cur]) tmp=qry2(Root,y);
	if (l==r) return tmp;
	if (x<=mid) return tmp+qry1(lson,x,y);
	else return tmp+qry1(rson,x,y);
}
void ins(int cur,int l,int r,int L,int R,pro p)
{
	if (L<=l&&R>=r)
	{
		tg[cur]=tg[cur]+p;
		return;
	}
	if (L<=mid) ins(lson,L,R,p);
	if (R>mid) ins(rson,L,R,p);
}
pro qry(int cur,int l,int r,int x)
{
	if (l==r) return tg[cur];
	if (x<=mid) return tg[cur]+qry(lson,x);
	else return tg[cur]+qry(rson,x);
}
int main()
{
	inv[0]=inv[1]=1;
	for (int i=2;i1) ins1(root,1,y-1,y,z,tmp);
			if (z

T3

(这tm还带卡常数的撒???)

首先一个串(多项式系数)的平方,显然只剩下平方项,其他都带2。。。然后我们就get到了轻易获得一个串平方后结果的方法。

考虑对于多项式的次数倍增,每次如果次数乘2,显然就是平方。乘2加1就是在乘上一个原串。

——————————————————————————————————————————————————————————————

不妨先只考虑整个串的情况,考虑维护2^max(n+1,k)个不同的串的数量(匹配数)(额,就是维护max(n+1,k)位的各种不同串的数量)。

(之后M=max(n+1,k))(程序里M为这里-1)

先预处理出次数乘2时,结果串的前M位数和第2位到第M+1位数;

次数乘2加1时,算中间那两种(具体怎么中间看程序),然后开头可以用特殊姿势ka过去

然后你发现这样就可以轻易地从原来的串的长度=M的子串所有的串推到结果串所有长度=M的子串啦

用dp数组保存下来每个串出现的个数,然后发现结果串最后M位左右(其实我也不知道到底有几位,算算就好了)没有被统计,没错,然后暴力加就好了(手动滑稽)

最后加答案时因为(很有)可能k

——————————————————————————————————————————————————————————————

这样我们开心(绝望)地发现这道题并不是求全局的答案。不过可以转成求前缀,然后我想了n久没想出啥正常办法,想不会是一开始算的时候就已经限制好了,一看标称,还真tm是。。(晕)

lim数组记录此时(大概)到维护第几位可以等效于最后的要求的前limit位

然后让我绝望的是这(居然)是大概,还要各种对答案++--。。。。。。。

开个bitset维护最后面几位,在dp转移完后,强行对答案再进行修正(具体见代码,这段抄标程的,等以后完全搞懂了有空再补吧。。)

对于上面说的开头部分,似乎可能大概是现在初始串后面加上M个零,然后前面没有统计的只会是一堆零,然后注意每次转移后去掉dp[0]的一种

——————————————————————————————————————————————————————————————

然后讲道理这样就能过了,可我交到uoj上只有30,然后开了o2(不知道有没有效果)也就70(但多过的点全是2900ms+),我第n次绝望。。。。n次卡常数后卡过了(终于)

后来一天后发现标程似乎用了某种指针常数优化,随手加上,常数一下变为原来的五分之一。。。!!!(剧毒吧)


#pragma GCC optimize("O2")
#include 
#define gc getchar()
#define ll long long
#define N 20
using namespace std;
ll n,K,first,qry,M,Max,m,L,R,go0[1<'9') if (ch=='-') x=-1;
	ll s=ch-'0';
	while (ch=gc,ch>='0'&&ch<='9') s=s*10ll+ch-'0';
	return s*x;
}
inline int get()
{
	char ch;
	while (ch=gc,ch!='0'&&ch!='1');
	return ch-'0';
}
inline ll work(ll n,ll m,ll limit)
{
	ll xx=0,yy=1,len=n,i,ans=0,top=0,del,lim_now,*d1,*d2,*dp=d,*Dp=D;
	bitset<128> o=first<<(M+1),p,Max_j=(1ll<<(M+1))-1,Max_sq_j=(1ll<<((M+1)<<1))-1;
	for (lim[1]=limit,i=2;i<60;++i)
		lim[i]=max((lim[i-1]+1)>>1,M);
	for (i=0;i<=len;++i) ++dp[first<<(M-i)&Max];
	for (i=m;i>1;i>>=1) num[++top]=i&1;
	while (top)
	{
		swap(dp,Dp);
		lim_now=lim[top],p=0;
		if (num[top]) d1=go2,d2=go3;
		else d1=go0,d2=go1;
		for (i=0;i<=Max;++i)
			if (Dp[i])
			{
				dp[d1[i]]+=Dp[i];
				dp[d2[i]]+=Dp[i];
				Dp[i]=0;
			}
		for (i=0;i<=(M<<1|1);++i)
			p[i<<1]=o[i];
		len=(len<<1)+(num[top]?M:0);
		del=max(len-lim_now,0ll);
		if (num[top])
		{
			o=0;
			for (i=0;i<=M;++i)
				if (first>>i&1) o^=p<>((M<<2ll)+3-i)&Max_j).to_ulong()];
			for (i=0;i>((M<<2ll)+2-i)&Max_j).to_ulong()];
			o>>=M*3+1-del;
			o&=Max_sq_j;
		}
		else
		{
			for (i=0;i>(M*3ll+2-i)&Max_j).to_ulong()];
			o=p>>((M<<1ll|1)-del)&Max_sq_j;
		}
		dp[0]--,len-=del,top--;
	}
	for (i=0;i<(1ll<<(M-K));++i)
		ans+=dp[qry|i];
	for (i=0;i<=Max;i++) dp[i]=0;
	return ans; 
}
int main()
{
	register ll T=Read(),i,j,Now,now;
	while (T--)
	{
		n=Read(),m=Read(),K=Read()-1;
		R=n*m+1-Read(),L=n*m+1-Read();
		first=qry=0;
		M=max(n,K);
		for (i=0;i<=n;++i) first=first<<1|get();
		for (i=0;i<=K;++i) qry|=get()<<(M-i);
		Max=(1ll<<(M+1))-1;
		Max_sq=(1ll<<((M+1)<<1))-1;
		for (i=1;i<(1<<(M+1));++i)
		{
			Now=now=0;
			for (j=0;j<=M;++j) now|=(i>>j&1)<<(j<<1);
			go0[i]=now>>M;
			go1[i]=(now>>(M-1))&Max;
			for (j=0;j<=M;++j)
				if (first>>j&1) Now^=(now<>M&Max;
			go3[i]=(Now>>(M-1))&Max;
		}
		if (R-L


你可能感兴趣的:(赛后题解,bzoj)