• Vjudge contest 423849


    Problem A

    是我太菜没错了。

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

    既然可以有交,那么就将所有链的长度都设为最大,那么所有的链一共会覆盖 (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("
    ");
    		}
    		else{
    			printf("%d ",l);
    			for(int j=i;j<i+l;++j) printf("%d ",eul[j]);
    			printf("
    ");
    		}
    	}
    	for(int i=cnt+1;i<=k;++i) printf("1 1
    ");
    	return 0;
    }
    

    Problem B

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

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


    好像猜错了。。。

    依旧不会啊。。。


    ( ext{zjj}) 提示我了矩阵乘法,但是我觉得时间复杂度不太对,是 (O(n^3log_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
    ",tag,res);
    		if(tmp2.s[1].any()){
    			tmp1=tmp2,tag^=1;
    			res+=(1ll<<i);
    		}
    	}
    	if(res>1e18) printf("-1
    ");
    	else printf("%lld
    ",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
    "),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,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
    ",res),0;
    }
    

    Problem D

    你可以用线段树套 ( ext{set}) 来维护每一个点向下能到达的隔板位置,然后对于每一个隔板,求出其有球落在上面后可以对答案的贡献,这个可以从下向上 ( ext{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
    ",l,r);
    		// printf("%lld %lld %lld
    ",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?
    ");
    	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
    ",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
    ",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
    ",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
    ",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
    ",res),0;
    }
    

    Problem E

    这个数据范围感觉有点像 (O(n^3)) 的区间 ( ext{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
    ",res);
    	int cnt[2];cnt[0]=cnt[1]=-res;
    	// for(int i=0;i<(int)bag.size();++i) if(!used[i]) printf("---%lld
    ",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
    ",res);
    	return 0;
    }
    
  • 相关阅读:
    pig中将两列合并为一列:concat
    最小二乘法拟合二元多次曲线
    动态重新加载Class机制之代码测试
    從 Windows Form ComboBox、ListBox 或 CheckedListBox 控制項加入或移除項目
    C#控件一览表
    C#中combobox 和TreeView控件属性、事件、方法收集
    PHP 分页类 潇湘博客
    一个房屋中介业务建模的实例分析
    使用Limit参数优化MySQL查询 潇湘博客
    word中的字号与实际的字体大小一一对应的关系
  • 原文地址:https://www.cnblogs.com/Point-King/p/14480204.html
Copyright © 2020-2023  润新知