Vjudge contest 423849

Problem A

是我太菜没错了。

构造出一种可以用 \(k\) 个长度 \(\le \left\lceil \frac{2n}{k}\right\rceil\) 的链将整张图的所有点覆盖的方案,链可以有交。

既然可以有交,那么就将所有链的长度都设为最大,那么所有的链一共会覆盖 \(2n\) 个节点。

还有一点可以确定的是题目保证有解。

我是傻逼。

你搞一个欧拉序然后断成 \(k\) 段不就行了。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,M=2e5+5;
int n,m,k,l;bool vis[N];
struct Edge{int nxt,to;}e[M<<1];int fir[N];
void add(int u,int v,int i){e[i]=(Edge){fir[u],v},fir[u]=i;}
int eul[N<<1],cnt_eul=0,cnt=0;;
void dfs(int u){
	vis[u]=true,eul[++cnt_eul]=u;
	for(int i=fir[u];i;i=e[i].nxt){
		if(vis[e[i].to]) continue;
		dfs(e[i].to),eul[++cnt_eul]=u;
	}
}
int main(){
	cin>>n>>m>>k,l=ceil(2.0*n/k);
	for(int i=1;i<=m;++i){
		int u,v;scanf("%d%d",&u,&v);
		add(u,v,i<<1),add(v,u,i<<1|1);
	}
	dfs(1);
	for(int i=1;i<=cnt_eul;i+=l,cnt++){
		if(i+l>cnt_eul){
			printf("%d ",cnt_eul-i+1);
			for(int j=i;j<=cnt_eul;++j) printf("%d ",eul[j]);
			printf("\n");
		}
		else{
			printf("%d ",l);
			for(int j=i;j<i+l;++j) printf("%d ",eul[j]);
			printf("\n");
		}
	}
	for(int i=cnt+1;i<=k;++i) printf("1 1\n");
	return 0;
}

Problem B

我猜测一下,如果存在一条长度为 \(10^{18}\) 的路径的话,就是满足存在一个导出子图,其中每一个点都有 \(0\)\(1\) 两种出边,这个东西好像以前搞过。

如果不存在这么长的路径的话,就暴力去找,感觉是不会很长的?


好像猜错了。。。

依旧不会啊。。。


\(\text{zjj}\) 提示我了矩阵乘法,但是我觉得时间复杂度不太对,是 \(O(n^3\log_210^{18})\) 的?用 \(bitset\) 优化一波就是 \(O(\frac{n^3}{\omega}\log_210^{18})\) 的,感觉有点假。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=5e2+5;
int n,m;long long res=0;
struct Matrix{int n,m;bitset<N> s[N];};
Matrix operator * (const Matrix a,const Matrix b){
	Matrix res;
	res.n=a.n,res.m=b.m;
	for(int j=1;j<=res.m;++j){
		bitset<N> B,tmp;B.reset();
		for(int k=1;k<=b.n;++k) B[k]=b.s[k][j];
		for(int i=1;i<=res.n;++i)
		tmp=(a.s[i]&B),res.s[i][j]=tmp.any();
	}
	return res;
}
Matrix f[2][61];
signed main(){
	cin>>n>>m;
	for(int i=1;i<=m;++i){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		f[w][0].s[u][v]=true;
	}
	if(f[0][0].s[1].none()) return printf("0"),0;
	f[0][0].n=f[0][0].m=f[1][0].n=f[1][0].m=n;
	for(int i=1;i<=60;++i){
		f[0][i]=f[0][i-1]*f[1][i-1];
		f[1][i]=f[1][i-1]*f[0][i-1];
	}
	if(f[0][60].s[1].any()) return printf("-1"),0;
	Matrix tmp1,tmp2;tmp1.n=tmp1.m=n;
	for(int i=1;i<=n;++i) tmp1.s[i][i]=1;
	for(int i=59,tag=0;i>=0;--i){
		tmp2=tmp1*f[tag][i];
		// printf("%d %lld\n",tag,res);
		if(tmp2.s[1].any()){
			tmp1=tmp2,tag^=1;
			res+=(1ll<<i);
		}
	}
	if(res>1e18) printf("-1\n");
	else printf("%lld\n",res);
	return 0;
}

Problem C

考虑有多少个集合满足其中每两个元素的异或值都是集合中的元素。

然后这个东西等价于不同的线性基的个数。

我们考虑 \(dp\) 求解, \(f_{i,j}\) 表示到第 \(i\) 位有 \(j\) 个主元的方案个数,然后考虑给上面的点附上 \(0,1\)

发现对于有主元的位,其不同的方案数只能贡献 \(1\) ,如果没有主元,就需要考虑 \(0,1\) 的奇偶性。

再用数位 \(dp\) 常见的套路就可以了。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int M=35;
const int MOD=1e9+7;
int n,m,f[M][M][2],res=0;
int ksm(int x,int k){
	int res=1;
	for(;k;k>>=1,x=x*x%MOD)
	if(k&1) res=res*x%MOD;
	return res;
}
int fac[M],ifac[M];
int C(int n,int m){
	return fac[n]*ifac[m]%MOD*ifac[n-m]%MOD;
}
int cal(int n,bool tag){
	int res=0;
	for(int i=0;i<=n;++i)
	res+=((i&1)==tag)*C(n,i),res%=MOD;
	return res;
}
signed main(){
	cin>>n;
	if(!n) return printf("1\n"),0;
	for(int x=n;x;x>>=1) m++;
	fac[0]=ifac[0]=1;
	for(int i=1;i<=m;++i) fac[i]=fac[i-1]*i%MOD;
	for(int i=1;i<=m;++i) ifac[i]=ksm(fac[i],MOD-2);
	// printf("%lld %lld\n",n,m);
	f[m-1][1][1]=1,f[m-1][0][0]=1;
	for(int i=m-1;i>0;--i){
		if(n&(1ll<<(i-1))){
			for(int j=0;j<=m;++j){
				f[i-1][j][1]+=f[i][j][1]*cal(j,1)%MOD,f[i-1][j][1]%=MOD;
				f[i-1][j][0]+=f[i][j][1]*cal(j,0)%MOD,f[i-1][j][0]%=MOD;
				f[i-1][j+1][1]+=f[i][j][1],f[i-1][j+1][1]%=MOD;
			}
		}
		else{
			for(int j=0;j<=m;++j){
				f[i-1][j][1]+=f[i][j][1]*cal(j,0)%MOD,f[i-1][j][1]%=MOD;
			}
		}
		for(int j=0;j<=m;++j){
			f[i-1][j][0]+=f[i][j][0]*ksm(2,j)%MOD,f[i-1][j][0]%=MOD;
			f[i-1][j+1][0]+=f[i][j][0],f[i-1][j+1][0]%=MOD;
		}
	}
	for(int i=0;i<=m;++i) res+=f[0][i][0]+f[0][i][1],res%=MOD;
	return printf("%lld\n",res),0;
}

Problem D

你可以用线段树套 \(\text{set}\) 来维护每一个点向下能到达的隔板位置,然后对于每一个隔板,求出其有球落在上面后可以对答案的贡献,这个可以从下向上 \(\text{dp}\) ,最后统计一下答案就可以了。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+5;
const int MOD=1e9+7;
int h,w,n;
struct Barrier{int h,l,r,lim;}a[N];
bool cmp(Barrier a,Barrier b){return a.h<b.h;}
struct Data{int id,h,lim;};
bool operator < (Data a,Data b){return a.h>b.h;}
struct Seg_Tree{
	struct Node{set<Data> bag;}tr[N<<4];
	void init(){
		for(int i=0;i<(N<<4);++i)
		tr[i].bag.insert((Data){0,0,h+1});
	}
	void add(int u,int l,int r,int x,int y,Data z){
		if(x<=l&&r<=y) return (void)(tr[u].bag.insert(z));
		int mid=(l+r)>>1;
		if(x<=mid) add(u<<1,l,mid,x,y,z);
		if(y>mid) add(u<<1|1,mid+1,r,x,y,z);
		return ;
	}
	Data query(int u,int l,int r,int x,int z){
		while(tr[u].bag.begin()->lim<z)
		tr[u].bag.erase(tr[u].bag.begin());
		// printf("%lld %lld\n",l,r);
		// printf("%lld %lld %lld\n",tr[u].bag.begin()->id,tr[u].bag.begin()->h,tr[u].bag.begin()->lim);
		if(l==r) return *tr[u].bag.begin();
		int mid=(l+r)>>1;
		if(x<=mid) return min(*tr[u].bag.begin(),query(u<<1,l,mid,x,z));
		else return min(*tr[u].bag.begin(),query(u<<1|1,mid+1,r,x,z));
	}
}t;
int f[N],res=0;
signed main(){
	cin>>h>>w>>n;
	for(int i=1;i<=n;++i){
		scanf("%lld%lld%lld%lld",&a[i].h,&a[i].l,&a[i].r,&a[i].lim);
	}
	sort(a+1,a+1+n,cmp),f[0]=1,t.init();
	// printf("Are you kidding me?\n");
	for(int i=1;i<=n;++i){
		Data tmp;
		if(a[i].l!=1){
			tmp=t.query(1,1,w,a[i].l-1,a[i].h);
			// printf("%lld %lld %lld\n",tmp.id,tmp.h,tmp.lim);
			f[i]+=f[tmp.id],f[i]%=MOD;
		}
		else{
			tmp=t.query(1,1,w,a[i].r+1,a[i].h);
			// printf("%lld %lld %lld\n",tmp.id,tmp.h,tmp.lim);
			f[i]+=f[tmp.id],f[i]%=MOD;
		}
		if(a[i].r!=w){
			tmp=t.query(1,1,w,a[i].r+1,a[i].h);
			// printf("%lld %lld %lld\n",tmp.id,tmp.h,tmp.lim);
			f[i]+=f[tmp.id],f[i]%=MOD;
		}
		else{
			tmp=t.query(1,1,w,a[i].l-1,a[i].h);
			// printf("%lld %lld %lld\n",tmp.id,tmp.h,tmp.lim);
			f[i]+=f[tmp.id],f[i]%=MOD;
		}
		tmp=(Data){i,a[i].h,a[i].h+a[i].lim};
		t.add(1,1,w,a[i].l,a[i].r,tmp);
	}
	for(int i=1;i<=w;++i){
		Data tmp=t.query(1,1,w,i,h+1);
		res+=f[tmp.id],res%=MOD;
	}
	return printf("%lld\n",res),0;
}

Problem E

这个数据范围感觉有点像 \(O(n^3)\) 的区间 \(\text{dp}\) ?但是没有什么思路?


发现就是一个网络流加贪心的过程,搞一下就好了。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=205,L=1e7+5;
const int INF=1e9+7;
int n,x[N],y[N];
bitset<L> tag;vector<int> pri,bag;
int from,to,tot=0,id[N],mp[N][N];
struct Edge{int nxt,to,flow;};vector<Edge> e;int fir[N];
void add(int u,int v,int w){
	e.push_back((Edge){fir[u],v,w}),fir[u]=e.size()-1;
}
int dis[N],cur[N],res=0;
queue<int> q;bool vis[N],used[N];
bool bfs(){
	for(int i=1;i<=tot;++i) dis[i]=INF,cur[i]=fir[i];
	dis[from]=0,vis[from]=true,q.push(from);
	while(!q.empty()){
		int u=q.front();vis[u]=false,q.pop();
		for(int i=fir[u];i>=0;i=e[i].nxt){
			int v=e[i].to;
			if(!e[i].flow||dis[v]<dis[u]+1) continue;
			dis[v]=dis[u]+1;if(!vis[v]) vis[v]=true,q.push(v);
		}
	}
	return dis[to]!=INF;
}
int dfs(int u,int flow){
	if(u==to) return flow;
	int res=0;vis[u]=true;
	for(int i=cur[u];i>=0&&flow;i=e[i].nxt){
		int v=e[i].to;cur[u]=i;
		if(!e[i].flow||dis[v]!=dis[u]+1||vis[v]) continue;
		int tmp=dfs(v,min(flow,e[i].flow));
		e[i].flow-=tmp,e[i^1].flow+=tmp,flow-=tmp,res+=tmp;
	}
	return vis[u]=false,res;
}
signed main(){
	for(int i=2;i<L;++i){
		if(!tag[i]) pri.push_back(i);
		for(int j=0;j<(int)pri.size();++j){
			if(i*pri[j]>=L) break;
			tag[i*pri[j]]=true;
			if(i%pri[j]==0) break;
		}
	}
	tag[0]=tag[1]=tag[2]=true;
	cin>>n;
	for(int i=1;i<=n;++i) scanf("%lld",&x[i]),y[i]=x[i]+1;
	for(int i=1;i<=n;++i) if(y[i]==x[i+1]) y[i]=x[i+1]=0;
	for(int i=1;i<=n;++i){
		if(x[i]) bag.push_back(x[i]);
		if(y[i]) bag.push_back(y[i]);
	}
	from=++tot,to=++tot;
	memset(fir,-1,sizeof(fir));
	for(int i=0;i<(int)bag.size();++i){
		id[i]=++tot;
		if(bag[i]&1) add(from,id[i],1),add(id[i],from,0);
		else add(id[i],to,1),add(to,id[i],0);
	}
	for(int i=0;i<(int)bag.size();++i){
		for(int j=i+1;j<(int)bag.size();++j){
			if(!tag[abs(bag[i]-bag[j])]){
				if(bag[i]&1) add(id[i],id[j],1),add(id[j],id[i],0);
				else add(id[j],id[i],1),add(id[i],id[j],0);
			}
		}
	}
	while(bfs()) res+=dfs(from,INF);
	// printf("%lld\n",res);
	int cnt[2];cnt[0]=cnt[1]=-res;
	// for(int i=0;i<(int)bag.size();++i) if(!used[i]) printf("---%lld\n",bag[i]);
	for(int i=0;i<(int)bag.size();++i) cnt[bag[i]&1]+=(!used[i]);
	res+=cnt[0]/2*2,res+=cnt[1]/2*2,cnt[0]%=2,cnt[1]%=2;
	if(cnt[0]&&cnt[1]) res+=3;
	printf("%lld\n",res);
	return 0;
}
posted @ 2021-03-04 14:44  Point_King  阅读(108)  评论(0编辑  收藏  举报