Loading

noip模拟51

A. 茅山道术

比较智障的一道\(dp\),然而我没想到.

老感觉像个数学题..

A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
	#define ll long long int
	#define lf double
	#define re register ll 
	#define ull unsigned ll
	#define mp make_pair
	#define lb lower_bound 
	#define ub upper_bound 
	#define Fill(x,y) memset(x,y,sizeof x)
	#define Copy(x,y) memcpy(x,y,sizeof x)
	#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
	inline ll read()
	{
		ll ss=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
		while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
		return cit?ss:-ss;
	}
} using namespace BSS;

const ll N=1e6+21,mod=1e9+7;

ll n,m,ans;
ll dp[N],val[N],pre[N],tot[N];
signed main(){
	File(magic.in,magic.out);
	n=read(); ll tmp=0,cnt=0;
	for(re i=1;i<=n;i++) if((tmp=read())!=val[cnt]) val[++cnt]=tmp;
	n=cnt;
	for(re i=1;i<=n;i++){
		dp[i]=(pre[val[i]]+tot[val[i]]+dp[i-1])%mod;
		tot[val[i]]++,pre[val[i]]=(pre[val[i]]+dp[i-1])%mod;
	}
	printf("%lld\n",(dp[n]+1)%mod);
	exit(0);
}

B. 泰拳警告

比较智障的一道数学题,乱画画就行.

B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
	#define ll long long int
	#define lf double
	#define re register ll 
	#define ull unsigned ll
	#define mp make_pair
	#define lb lower_bound 
	#define ub upper_bound 
	#define Fill(x,y) memset(x,y,sizeof x)
	#define Copy(x,y) memcpy(x,y,sizeof x)
	#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
	inline ll read()
	{
		ll ss=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
		while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
		return cit?ss:-ss;
	}
} using namespace BSS;

const ll mod=998244353,N=3e6+21;

ll m,n,wl,pj,ans,pow2;
ll frac[N],poj[N],pol[N],pot[N],inv[N],dn[N],c[N];
inline ll ksm(ll a,ll b,ll c){
	a%=c; ll res=1;
	while(b){
		if(b&1) res=(res*a)%mod;
		a=(a*a)%mod,b>>=1;
	}
	return res%mod;
}
inline ll C(ll a,ll b){
	if(a>b) return 0;
	return frac[b]*ksm(frac[a]*frac[b-a],mod-2,mod)%mod;
}
signed main(){
	freopen("fight.in","r",stdin),freopen("fight.out","w",stdout);
	n=read(),m=read(),wl=ksm(m+2,mod-2,mod),pj=wl*m%mod,
	pow2=ksm(2,mod-2,mod),
	frac[0]=1,poj[0]=1,pol[0]=1,pot[0]=1,dn[0]=1,c[0]=1,inv[1]=1;
	for(ll i=1;i<=3000000;i++){
		frac[i]=frac[i-1]*i%mod,pot[i]=(pot[i-1]<<1)%mod,
		poj[i]=poj[i-1]*pj%mod,pol[i]=pol[i-1]*wl%mod;
		if(i>1) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	}
	for(re i=0;i<=n;i++){
		dn[i+2]=dn[i]*(i+1)%mod*(i+2)%mod,
		dn[i+2]=dn[i+2]*inv[(i>>1)+1]%mod*inv[(i>>1)+1]%mod,
		c[i+1]=c[i]*(n-i)%mod*inv[i+1]%mod;
	}
	ll res,tmp;
 	for(ll i=0;i<n;i++){
		tmp=n-i;
		res=pot[tmp]-dn[tmp]*(!(tmp&1))+mod,res=res%mod*pow2%mod,
		res=res*pol[tmp]%mod*poj[i]%mod*c[i]%mod;
		ans=(ans+res*(i+1)%mod)%mod;
	}	
	printf("%lld\n",ans);
	exit(0);
}

C. 万猪拱塔

发现很难直接做,考虑如何转化问题.

发现一个显然但不明显的性质:

某个矩形如果合法,那么这个矩形的权值一定是连续的.

发现一个很不明显的性质,

我们把整个矩形有重叠地划分为\((n+1)*(m+1)\)个小的\(2*2\)正方块.

如果我们把这个矩形进行染色,那么就只有四个小正方块的内部被染色了一个格子;同样的,如果对 \(l\) ~ \(r\) 染色,只有四个小正方块的内部被染了一个格子,且没有内部被染了三个格子的正方块,就说明这是一个矩形.即为充要条件.

定义一个 \(f_{l,r}\) 数组为,如果把权值位于 \(l\) ~ \(r\) 全都染色,有多少个小正方块内部含有奇数个被染色的格子.

考虑在改变 \(r\) 时更新 \(l\) 的值,发现只会对权值为 \(r\) 的点所被包含四个小正方形有影响.

这边建议自己举例乱糊一下,不再赘述了..

于是线段树维护即可.

C_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
	#define ll long long int
	#define lf double
	#define re register ll 
	#define ull unsigned ll
	#define mp make_pair
	#define lb lower_bound 
	#define ub upper_bound 
	#define Fill(x,y) memset(x,y,sizeof x)
	#define Copy(x,y) memcpy(x,y,sizeof x)
	#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
	inline ll read()
	{
		ll ss=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
		while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
		return cit?ss:-ss;
	}
} using namespace BSS;

#define ls x<<1
#define rs x<<1|1
const ll mod=998244353,N=2e5+21;

ll m,n,ans;
ll c[25];
unordered_map<ll,ll> val[N];
struct I { ll cnt,lzy,sumw,minw; } tr[N<<2],ret;
struct II { ll x,y; } p[N];
inline void getval(ll x,ll w){ 
	tr[x].minw+=w,tr[x].lzy+=w;
}
inline void spread(ll x,ll l,ll r){
	ll &lzy=tr[x].lzy,mid=(l+r)>>1;
	if(lzy) getval(ls,lzy),getval(rs,lzy),lzy=0;
}
inline void pushup(ll x){
	ll &minw=tr[x].minw; 
	minw=min(tr[ls].minw,tr[rs].minw);
	tr[x].cnt=tr[ls].cnt*(minw==tr[ls].minw)+tr[rs].cnt*(minw==tr[rs].minw),
	tr[x].sumw=tr[ls].sumw*(minw==tr[ls].minw)+tr[rs].sumw*(minw==tr[rs].minw);
}
void update(ll x,ll l,ll r,ll ql,ll qr,ll w){
	if(ql>qr) return;
	if(l>=ql and r<=qr) return getval(x,w),void();
	ll mid=(l+r)>>1; spread(x,l,r);
	if(ql<=mid) update(ls,l,mid,ql,qr,w);
	if(qr>mid) update(rs,mid+1,r,ql,qr,w);
	pushup(x);
}
void pushdown(ll x,ll l,ll r,ll ql,ll qr){
	if(l>=ql and r<=qr){
		if(tr[x].minw==4) ret.sumw+=tr[x].sumw,ret.cnt+=tr[x].cnt;
		return ;
	}
	ll mid=(l+r)>>1; spread(x,l,r);
	if(ql<=mid) pushdown(ls,l,mid,ql,qr);
	if(qr>mid) pushdown(rs,mid+1,r,ql,qr);
	pushup(x);
}
void build(ll x,ll l,ll r){
	if(l==r) return tr[x].cnt=1,tr[x].sumw=l,void();
	ll mid=(l+r)>>1;
	build(ls,l,mid),build(rs,mid+1,r);
	pushup(x);
}
inline void solve(ll x,ll y,ll w){
	ll cnt=0,p,k=1;
	c[++cnt]=val[x][y],c[++cnt]=val[x-1][y-1],
	c[++cnt]=val[x-1][y],c[++cnt]=val[x][y-1];
	sort(c+1,c+1+cnt),p=lb(c+1,c+1+cnt,w)-c;
	for(p;p>=1;p--,k*=-1){
		update(1,1,n,c[p-1]+1,c[p],k);
	}
}
signed main(){
	File(pig.in,pig.out);
	n=read(),m=read(); ll w,x,y,cnt;
	for(int i=0;i<=n+1;i++) 
		val[0][i]=N,val[n+1][i]=N,val[i][0]=N,val[i][n+1]=N;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++) 
			val[i][j]=w=read(),p[w].x=i,p[w].y=j;
	}
	n*=m,build(1,1,n);
	for(re i=1;i<=n;i++){
		solve(p[i].x,p[i].y,i),solve(p[i].x+1,p[i].y+1,i),
		solve(p[i].x+1,p[i].y,i),solve(p[i].x,p[i].y+1,i);
		pushdown(1,1,n,1,i);
	//	cout<<ret.cnt<<' '<<ret.sumw<<endl;
	//	cout<<"res:"<<(i+1)*ret.cnt-ret.sumw<<endl;
	//	cout<<tr[1].minw<<endl;
 		ans=(ans+(i+1)*ret.cnt-ret.sumw+mod)%mod;
		ret.cnt=0,ret.sumw=0;
	}
	printf("%lld\n",ans);
	exit(0);
}

D. 抑郁刀法

考虑推一推性质.

发现对于一些出入度等于 \(1\) 的边来说.

我们选择删掉 \(ta\) 们之后再统计答案,最后再乘上 \(k-1\) 就好了.

到这里其实是应该再想一想就能 \(get\) 到正解的.

发现数据范围给出的 \(m\)\(n\) 很接近.

所以是不是删掉 \(d=2\) 的点也行..?

发现的确就是这样,然而我没想到,考场上也确实没有这么多时间思考.

沿着上面的思路,考虑删掉 \(d=2\) 的点,

但是要再在两个点之间连一条新边,并赋个权.

分别记录两个点为 \(a,b\).

\(f_{a,b}\)\(a,b\) 同色时答案乘的系数,\(g_{a,b}\)\(a,b\) 异色时答案乘的系数.

但是当要删的两条边也有权,那就要算一算了.

设要删的两条边原来的 \(f\)\(g\) ,分别为 \(f_1,g_1,f_2,g_2\).

那么新的 \(f=(k-1)*g_1*g_2+f_1*f_2\)\(g=(k-2)*g_1*g_2+f_1*g_2+f_2*g_1\).

删完之后暴力做状压就可以了.

另外,状压的时候枚举的范围注意不要写成 \(n\)\(k\),合并边的时候注意要先把之前的 \(f\)\(g\) 乘起来再把之前的边删掉.

关于如何想到..

其实看到 \(n\) 给出了 \(\le 10\) 的数据之后发现 \(m\le n+5\)

就可以逆向考虑这个题目了,

大概就是暴力不能直接做,那就选择把题目数据变成暴力的思想.

D_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
	#define ll long long int
	#define lf double
	#define ull unsigned ll
	#define mp make_pair
	#define lb lower_bound 
	#define ub upper_bound 
	#define Fill(x,y) memset(x,y,sizeof x)
	#define Copy(x,y) memcpy(x,y,sizeof x)
	#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
	#define FILE(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
	inline ll read()
	{
		ll ss=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
		while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
		return cit?ss:-ss;
	}
} using namespace BSS;

const int N=1e5+21,mod=1e9+7;

ll m,n,r,ans,sum,ts=1,cnt;
ll frc[N],ban[N],head[N],vis[N],d[N],rk[N],pot[N];
ll dp[1<<17][17];
struct I { ll u,v,f,g,nxt,lst; } e[N<<3];
queue<ll> que;
map<ll,ll> mp1[N];
inline ll ksm(ll a,ll b,ll c){
	a%=c; ll res=1;
	while(b){
		if(b&1) res=(res*a)%c;
		a=(a*a)%c,b>>=1;
	}
	return res%c;
}
inline ll C(ll a,ll b){
	if(a>b) return 0;
	return frc[b]*ksm(frc[a]*frc[b-a],mod-2,mod)%mod;
}
inline void del(ll i){
	ll x,y;
	x=e[i].lst,y=e[i].nxt;
	e[x].nxt=y,e[y].lst=x;
	if(i==head[e[i].u]) head[e[i].u]=y;
}
inline void add(ll u,ll v,ll f,ll g){
	e[++ts].u=u,e[ts].v=v,e[ts].nxt=head[u],e[head[u]].lst=ts;
	head[u]=ts;
	if(mp1[u][v]==0)e[ts].f=f,e[ts].g=g,mp1[u][v]=ts;
	else e[ts].f=f*e[mp1[u][v]].f%mod,e[ts].g=g*e[mp1[u][v]].g%mod,del(mp1[u][v]);
//	cout<<u<<' '<<v<<' '<<e[ts].f<<" "<<e[ts].g<<endl;
	mp1[u][v]=ts;
}
inline void bfs(){
	for(int i=1;i<=n;i++){
		if(d[i]==1) que.push(i),sum=sum*(r-1)%mod,vis[i]=1;
	}
	ll u,v,v1,v2,f1,f2,g1,g2,f,g;
	while(que.size()){
		u=que.front(),que.pop();
		for(int i=head[u];i;i=e[i].nxt){
			if(vis[e[i].v]) continue;
			if((--d[e[i].v])==1){
				que.push(e[i].v),sum=sum*(r-1)%mod,vis[e[i].v]=1;
			}
			else del(i),del(i^1);
		}
	}
	for(int u=1;u<=n;u++){
		if(d[u]==2){
			v1=e[head[u]].v,f1=e[head[u]].f,g1=e[head[u]].g,del(head[u]^1),del(head[u]);
			v2=e[head[u]].v,f2=e[head[u]].f,g2=e[head[u]].g,del(head[u]^1),del(head[u]);	
			if((!v1) or (!v2)) continue;
			f=(r-1)*g1%mod*g2%mod+f1*f2%mod,g=(r-2)*g1%mod*g2%mod+f1*g2%mod+f2*g1%mod,f%=mod,g%=mod;
			add(v1,v2,f,g),add(v2,v1,f,g);
			vis[u]=1;
		}
	}
}
signed main(){
	n=read(),m=read(),r=read(),frc[0]=1,sum=1; ll u,v,U,S,res;
	for(ll i=1;i<=1e5;i++) frc[i]=frc[i-1]*i%mod;
	for(int i=1;i<=m;i++){
		u=read(),v=read(),d[u]++,d[v]++;
		add(u,v,0,1),add(v,u,0,1);
	}
	bfs();
	for(int i=1;i<=n;i++){
		if(!vis[i]) pot[++cnt]=i,rk[i]=cnt;
	}	
	dp[0][0]=1; U=(1<<cnt)-1;
	for(int i=0;i<=U;i++){
		S=U xor i; 
		for(int s=S;s;(--s)&=S){
			res=1;
			for(int j=1;j<=cnt;j++){
				if(!((s>>j-1)&1)) continue;
				for(int k=j;k<=cnt;k++){
					if(!((s>>k-1)&1)) continue;
					if(mp1[pot[j]].find(pot[k])==mp1[pot[j]].end()) continue;
					res=(res*e[mp1[pot[j]][pot[k]]].f)%mod;
				}
				for(int k=1;k<=cnt;k++){
					if(!((i>>k-1)&1)) continue;
					if(mp1[pot[j]].find(pot[k])==mp1[pot[j]].end()) continue;
					res=(res*e[mp1[pot[j]][pot[k]]].g)%mod;
				}
			}
			for(int j=0;j<cnt;j++){
				dp[i|s][j+1]=(dp[i|s][j+1]+dp[i][j]*res%mod)%mod;
			}
		}
	}
	for(int i=1;i<=r;i++) ans=(ans+dp[U][i]*C(i,r)%mod)%mod;
	if(!cnt) ans=r*ksm(r-1,mod-2,mod)%mod; ans=ans*sum%mod;
	printf("%lld\n",ans),exit(0);
}
posted @ 2021-09-15 07:08  AaMuXiiiiii  阅读(68)  评论(1)    收藏  举报