noip模拟48[脑袋好像有问题了]

noip模拟48 solutions

我觉得我真是SB了,我也能有一气挂他个100分的时候

不知道为啥,我取完模之后直接>>=1

咱也不知道咋想的,然后100->17

害以后还是少干这种完蛋的事吧

T1 Lighthouse

这个他们都说是普通容斥,还有说子集反演的

我就觉得是二项式反演,yyds

\(f_i\)表示至少加入了i条不合法的边的方案数,我们最后求的就是\(g_0\)

直接上反演就行了,化简之后就是偶加奇减

这个f求得时候不能直接圆排列,还要考虑不合法的情况

一个点上连了三条边,肯定不能构成环

里面出现了一个小环,也不合法,但是如果这个环就是n个点的环的话是合法的

AC_code


#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=1e7+5;
const int M=205;
const ll mod=1e9+7;
int n,m;
int fr[M],to[M];
ll f[M],jc[N],tc[N],ans;
ll mst[N],mnt;
int vis[N];
ll ksm(ll x,ll y){
	ll ret=1;
	while(y){
		if(y&1)ret=ret*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return ret;
}
signed main(){
	scanf("%d%d",&n,&m);mnt=0;
	for(re i=1;i<=m;i++)scanf("%d%d",&fr[i],&to[i]);
	jc[0]=1;for(re i=1;i<=n;i++)jc[i]=jc[i-1]*i%mod;
	tc[0]=1;for(re i=1;i<=n;i++)tc[i]=tc[i-1]*2%mod;
	for(re s=0;s<(1<<m);s++){
		int sp=0,se=0,flag=1;
		for(re i=1;i<=m;i++){
			if(!((s>>i-1)&1))continue;se++;
			if(vis[fr[i]]==2||vis[to[i]]==2){flag=0;break;}
			if(!vis[fr[i]])sp++;vis[fr[i]]++;
			if(!vis[to[i]])sp++;vis[to[i]]++;
		}//cout<<flag<<" ";
		for(re i=1;i<=mnt;i++)if((s&mst[i])==mst[i])flag=0;
		if((sp==se)&&(sp!=n)&&(flag)&&(s)){mst[++mnt]=s;flag=0;}
		if((sp==se)&&(sp==n)&&(flag)&&(s)){
			mst[++mnt]=s;flag=0;
			f[n]=(f[n]+2)%mod;
		}
		for(re i=1;i<=m;i++){
			if(!((s>>i-1)&1))continue;
			vis[fr[i]]=0;vis[to[i]]=0;
		}
		if(!flag)continue;
		f[se]=(f[se]+jc[n-se-1]*tc[sp-se])%mod;
	}
	//cout<<mnt<<endl;
	/*for(re i=1;i<=mnt;i++){
		for(re j=1;j<=m;j++)
			if((mst[i]>>j-1)&1)cout<<"1";
			else cout<<"0";
		cout<<endl;
	}*/
	//for(re i=0;i<=m;i++)cout<<f[i]<<" ";cout<<endl;
	for(re i=0;i<=m;i++)f[i]=f[i]*ksm(2,mod-2)%mod;//cout<<f[i]<<" ";cout<<endl;
	for(re i=0,xs=1;i<=m;i++,xs=-xs)ans=(ans+xs*f[i]+mod)%mod;
	printf("%lld",ans);
}

T2 Miner

我真的没看出来这个是欧拉路,写的很隐蔽啊

我们要不重不漏的经过每一条边,

就像题解说的,我们找到这个图中的每一个联通块

我们要用一些边把这些块串联起来

每个块内只可能有偶数个奇数度数的点,我们每连一条边就会少两个奇数的点

我们就直接给每个块的奇数的个数/2,设个数为\(c_i\)

\[\sum\limits\max(1,\frac{c_i}{2})-1 \]

减一就是因为我们可以留下两个奇数的点,不是回路,而是欧拉路

然后我们随便调出几个点先全部串起来,再瞎连就行了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1e5+5;
const int M=1e5+5;
int n,m;
int to[M*10],nxt[M*10],head[N],rp=1,val[M*10];
void add_edg(int x,int y,int z){
	to[++rp]=y;
	val[rp]=z;
	nxt[rp]=head[x];
	head[x]=rp;
}
int c[N],cc,du[N];
bool vis[N];
vector<int> vec[N];
int ji[N],cnt;
void dfs(int x){
	vis[x]=true;
	if(du[x]&1){
		c[cc]++;
		vec[cc].push_back(x);
	}
	for(re i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(vis[y])continue;
		dfs(y);
	}
}
struct node{
	int go,ho;
	node(){}
	node(int x,int y){go=x;ho=y;}
}sta[M*10],ans[M*10];
int top,an,ans1;
bool via[M*10];
void oular(){
	sta[++top]=node(vec[1][1],0);
	while(top>0){
		//cout<<top<<" "<<sta[top].go<<endl;
		int x=sta[top].go,i=head[x];
		while(i&&via[i])i=nxt[i];
		if(i){
			sta[++top]=node(to[i],val[i]);
			via[i]=via[i^1]=true;
			head[x]=nxt[i];
		}
		else{
			ans[++an]=sta[top];top--;
		}
	}
}
signed main(){
	scanf("%d%d",&n,&m);
	for(re i=1,x,y;i<=m;i++){
		scanf("%d%d",&x,&y);
		add_edg(x,y,0);add_edg(y,x,0);
		du[x]++;du[y]++;
	}
	for(re i=1;i<=n;i++)
		if(!vis[i]&&head[i]){
			cc++;vec[cc].push_back(0);dfs(i);
			if(c[cc]==0)vec[cc].push_back(i),vec[cc].push_back(i);
			ans1+=max(c[cc]/2,1);
		}
	//cout<<cc<<endl;
	for(re j=3;j<=c[1];j++)ji[++cnt]=vec[1][j];
	for(re i=2;i<=cc;i++){
		add_edg(vec[i-1][2],vec[i][1],1);
		add_edg(vec[i][1],vec[i-1][2],1);
		//cout<<vec[i-1][2]<<" "<<vec[i][1]<<endl;
		for(re j=3;j<=c[i];j++)ji[++cnt]=vec[i][j];
	}
	//cout<<cnt<<endl;
	for(re i=2;i<=cnt;i+=2){
		add_edg(ji[i-1],ji[i],1);
		add_edg(ji[i],ji[i-1],1);
	}
	printf("%d\n",ans1-1);
	oular();
	printf("%d\n",ans[an].go);
	for(re i=an-1;i>=1;i--)printf("%d %d\n",ans[i].ho,ans[i].go);
}

T3 Lyk Love painting

这个dp我考场上想都没想直接弃了,没想到这么简单

基础dp就不说了

一看是最大值最小,那肯定是二分了

我们用dp判断是否合法

当前二分值就是限制,我们直接按照这个限制向后转移,

分别转移上面的,下面的,上下一起的

上下一起的直接找到距离当前位置最远的合法的点

上面和下面的直接跳着往前走,因为越靠前越优,大的向前跳

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=1e5+5;
const int M=105;
ll n,m;
ll fail[N][4],pre[N][4],dp[N];
bool check(ll lim){
	fail[0][1]=fail[0][2]=fail[0][3]=0;
	for(re i=1;i<=n;i++)
		for(re j=1;j<=3;j++){
			fail[i][j]=fail[i-1][j];
			while(pre[i][j]-pre[fail[i][j]][j]>lim)fail[i][j]++;
		}
	memset(dp,0x3f,sizeof(dp));dp[0]=0;
	for(re i=1;i<=n;i++){
		dp[i]=min(dp[i],dp[fail[i][3]]+1);
		ll i1=i,i2=i,no=0;
		while(no+1<=m&&(i1||i2)){
			if(i1<i2)i2=fail[i2][2];
			else i1=fail[i1][1];no++;
			dp[i]=min(dp[i],dp[max(i1,i2)]+no);
		}
		if(dp[i]>m)return false;
	}
	return true;
}
signed main(){
	scanf("%lld%lld",&n,&m);
	for(re i=1;i<=n;i++){ll x;scanf("%lld",&x),pre[i][1]=x,pre[i][3]+=x;}
	for(re i=1;i<=n;i++){ll x;scanf("%lld",&x),pre[i][2]=x,pre[i][3]+=x;}
	for(re i=1;i<=n;i++)for(re j=1;j<=3;j++)pre[i][j]+=pre[i-1][j];
	ll l=0,r=pre[n][3],mid;
	while(l<r){
		mid=l+r>>1;//cout<<l<<" "<<r<<" "<<mid<<endl;
		if(check(mid))r=mid;
		else l=mid+1;
	}
	printf("%lld",l);
}

T4 Revive

颓废状态,暂放

posted @ 2021-08-27 07:24  fengwu2005  阅读(58)  评论(0编辑  收藏  举报