2018.12.08【NOIP提高组】模拟B组

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sugar_free_mint/article/details/84993130

JZOJ 5123 diyiti

bzoj 4927 链接

分析

6根木棍,只能是3+1+1+1或者是2+2+1+1,所以分类讨论。(以下其它情况都排除了之前的情况,也就是容斥,为了行文方便,在此不多写)
设边长为 x x i × 2 i\times2 代表 i i i和i

2+2+1+1的组合方式
x , x , x 2 × 2 , x 2 × 2 ( x ) x,x,\frac{x}{2}\times 2,\frac{x}{2}\times 2(x是偶数)
以下保证 t × 2 < x t\times2<x
x , x , x t t , x t t x,x,x-t和t,x-t和t
x , x , x t 1 t 1 , x t 2 t 2 t 1 t 2 x,x,x-t_1和t_1,x-t_2和t_2(t_1\neq t_2)
x , x , x t t , x 2 × 2 ( x ) x,x,x-t和t,\frac{x}{2}\times 2(x是偶数)
3+1+1+1的组合方式
x , x , x , x 3 × 3 ( x 3 ) x,x,x,\frac{x}{3}\times3(x能被3整除)
x , x , x , x t 1 × 2 t 1 t 1 x,x,x,x-t_1\times 2和t_1和t_1
x , x , x , x t 1 t 2 t 1 t 2 ( x t 1 t 2 t 1 t 2 ) x,x,x,x-t_1-t_2和t_1和t_2(x-t_1-t_2\neq t_1\neq t_2)

这样讲好像很简单,实际上实践会比较容易在细节上出问题,我的细节出错点详见代码


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <vector>
#define rr register
using namespace std;
const int N=10000001;
typedef long long ll; ll ans;
vector<int>t; int cnt[N+10],dcnt[N+10];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
signed main(){
	freopen("yist.in","r",stdin);
	freopen("yist.out","w",stdout);
	for (rr int n=iut(),x;n;--n)
	    if (++cnt[x=iut()]==1) t.push_back(x);
	sort(t.begin(),t.end());
	for (rr int i=0;i<t.size()-1;++i)
	    for (rr int j=i+1;j<t.size();++j)
	        if (t[i]+t[j]<N)
			    dcnt[t[i]+t[j]]+=cnt[t[i]]*cnt[t[j]];//原代码只是=
			else break;
	for (rr int i=1;i<t.size();++i){
		if (cnt[t[i]]==1) continue;
		if (cnt[t[i]]>1){
			rr ll x=1ll*cnt[t[i]]*(cnt[t[i]]-1)>>1,sum=0;
			if (!(t[i]&1)&&cnt[t[i]>>1]>3)
			    ans+=x*cnt[t[i]>>1]*(cnt[t[i]>>1]-1)*(cnt[t[i]>>1]-2)*(cnt[t[i]>>1]-3)/3>>3;//原代码判成奇数
			for (rr int j=0;j<i;++j)
			if ((t[j]<<1)<t[i]){
				if (cnt[t[j]]>1&&cnt[t[i]-t[j]]>1)
				    sum+=1ll*cnt[t[j]]*(cnt[t[j]]-1)*cnt[t[i]-t[j]]*(cnt[t[i]-t[j]]-1)>>2;
			}else break;
			ans+=x*sum; sum=0;
			for (rr int j=0;j<i;++j)
			if ((t[j]<<1)<t[i]){
			    if (cnt[t[i]-t[j]])
			        ans+=x*sum*cnt[t[j]]*cnt[t[i]-t[j]],sum+=cnt[t[j]]*cnt[t[i]-t[j]];
			}else break;
			if (!(t[i]&1)) ans+=x*sum*cnt[t[i]>>1]*(cnt[t[i]>>1]-1)>>1;//原代码未判断
		}
		if (cnt[t[i]]==2) continue; 
		if (cnt[t[i]]>2){
			rr ll x=1ll*cnt[t[i]]*(cnt[t[i]]-1)*(cnt[t[i]]-2)/3>>1,sum=0;
			if (t[i]%3==0&&cnt[t[i]/3]>2)
			    ans+=x*cnt[t[i]/3]*(cnt[t[i]/3]-1)*(cnt[t[i]/3]-2)/3>>1;
			for (rr int j=0;j<i;++j){
				sum+=1ll*cnt[t[j]]*dcnt[t[i]-t[j]];
				if ((t[j]<<1)<t[i]&&t[j]*3!=t[i])
				    sum-=1ll*cnt[t[j]]*cnt[t[j]]*cnt[t[i]-(t[j]<<1)];
			}
			ans+=x*sum/3;
			for (rr int j=0;j<i;++j)
			if ((t[j]<<1)<t[i]){
				if (t[j]*3!=t[i]) ans+=x*cnt[t[j]]*(cnt[t[j]]-1)*cnt[t[i]-(t[j]<<1)]>>1;
			}else break;
		}
	}
	return !printf("%lld",ans);
}

JZOJ 100042 保留道路

题目大意

n n 个点 m m 条边的无向图中,每条边都有两个属性g和s,如果建某条边,那么边权是 m a x { w G × g } + m a x { w S × s } max\{wG\times所有剩下道路的属性g\}+max\{wS\times所有剩下道路的属性s\} ,其中 w G wG w S wS 是给定的值。求该图的最小生成树


分析

枚举每一条边 i i ,固定最大的边为 g i g_i ,对于 s s 跑最小生成树,不过有一些可以剪枝的地方是之前没有用上的边用上不会更优,所以说大大优化了时间复杂度。


代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#define rr register
#define max(a,b) (((a)>(b))?(a):(b))
#define min(a,b) (((a)<(b))?(a):(b))
using namespace std;
struct node{
	int x,y,g,s,next;
	bool operator <(const node &t)const{
	    if (g!=t.g) return g<t.g;
	        else return s<t.s;
	}
}e[50001]; long long ws,wg,ans=1ll<<62;
int n,m,f[401],t,kr[50001],ls[401];//kr表示候选的编号(升序)
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline signed getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
inline void kruskal(int mg){
	rr bool v[t+1];	rr int cnt=1;
	memset(v,0,sizeof(v));
	for (rr int i=1;i<=n;++i) f[i]=i;
	for (rr int i=1;i<=t;++i){
		rr int tt=kr[i],fa=getf(e[tt].x),fb=getf(e[tt].y);
		if (fa!=fb)
			++cnt,v[i]=1,
			f[min(fa,fb)]=max(fa,fb);
		if (cnt==n){
			rr int tot=0;
			for (rr int j=1;j<=t;++j) if (v[j]) kr[++tot]=kr[j];//没有最小生成树那么肯定会被覆盖掉
			fill(kr+1+tot,kr+1+t,0);
			t=tot; ans=min(ans,wg*mg+ws*e[tt].s);//既然升序答案就是最后一条边
			break;
		}
	}
}
signed main(){
	n=iut(); m=iut(); wg=iut(); ws=iut();
	for (rr int i=1;i<=m;++i){
		rr int x=iut(),y=iut();
		if (x==y) iut(),iut(),--i,--m;//处理自环
		else e[i]=(node){x,y,iut(),iut(),ls[x]},ls[x]=i;
	}
	sort(e+1,e+1+m);
	for (rr int i=1;i<=m;++i){
		if (wg*e[i].g+ws*e[i].s>=ans) continue;//肯定不会更优
		rr int l=1,r=++t;//二分插入排序
		while (l<r){
			rr int mid=(l+r)>>1;
			if (e[kr[mid]].s>e[i].s) r=mid;
			    else l=mid+1;
		}
		if (l==t) kr[t]=i;
		else{
			for (rr int i=t;i>l;--i) kr[i]=kr[i-1];
			kr[l]=i;
	    }
		kruskal(e[i].g);
	}
	printf("%lld",ans);
	return 0;
}

JZOJ 3518 进化序列

题目大意

有多少区间的按位或值小于 m m


分析

扫描每一个结尾,然后找到最大的开头


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
int n,m,a[100001],head=1,cnt[33]; long long ans;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
	return ans;
}
signed main(){
	freopen("evolve.in","r",stdin);
	freopen("evolve.out","w",stdout);
	n=iut(); m=iut();
	for (rr int i=1;i<=n;++i) a[i]=iut();
	for (rr int i=1;i<=n;++i){
		rr int sum=0;
		for (rr int j=0;j<32;++j) sum|=(cnt[j]>0)<<j;
		while ((sum|a[i])>=m&&head<=i){
			for (rr int j=0;j<32;++j)
			if (a[head]&(1<<j))
				if (!(--cnt[j])) sum^=1<<j;
			++head;
		}
		if (i>head) ans+=i-head;
		for (rr int j=0;j<32;++j)
		if (a[i]&(1<<j))
		    if (!cnt[j]++) sum|=1<<j;
	}
	printf("%lld",ans);
	return 0;
}

JZOJ 5223 B

题目

给定一个3*3的网格图,一开始每个格子上都站着一个机器人。每一步机器人可以走到相邻格子或留在原地,同一个格子上可以有多个机器人。问走n步后,有多少种走法,满足每个格子上都有机器人。答案对10^9+7取模。


分析

然而这是一道矩阵乘法的题目,机器人只能跑到相邻的格子,于是跑完矩阵乘法后枚举全排列,对于每种全排列用乘法原理求出答案


代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#define rr register
using namespace std;
int b[9]; typedef long long ll;
const ll mod=1000000007ll;
ll n,a[9][9],ans;
inline void mulself(ll f[9][9]){
	ll c[9][9];
	memset(c,0,sizeof(c));
	for (rr int i=0;i<9;++i)
	for (rr int j=0;j<9;++j)
	for (rr int k=0;k<9;++k)
	(c[i][j]+=f[i][k]*f[k][j]%mod)%=mod;
	memcpy(f,c,sizeof(c));
}
inline void mul(ll f[9][9],ll x){
	ll c[9]={0,0,0,0,0,0,0,0,0};
	for (rr int i=0;i<9;++i)
	for (rr int j=0;j<9;++j)
	(c[j]+=a[x][i]*f[i][j]%mod)%=mod;
	memcpy(a[x],c,sizeof(c));
}
inline void init(ll x,ll y){
	rr ll f[9][9]={//就是连相邻的边
	    {1,1,0,1,0,0,0,0,0},
		{1,1,1,0,1,0,0,0,0},
	    {0,1,1,0,0,1,0,0,0},
		{1,0,0,1,1,0,1,0,0},
		{0,1,0,1,1,1,0,1,0},
		{0,0,1,0,1,1,0,0,1},
		{0,0,0,1,0,0,1,1,0},
		{0,0,0,0,1,0,1,1,1},
		{0,0,0,0,0,1,0,1,1}
	};
	for (;y;mulself(f),y>>=1) if (y&1) mul(f,x);
}
signed main(){
	scanf("%lld",&n);
	for (rr int i=0;i<9;++i)
		a[i][i]=1,init(i,n),b[i]=i;
	do{
		rr ll mull=1;
		for (rr int i=0;i<9;++i) (mull*=a[i][b[i]])%=mod;//乘法原理
		(ans+=mull)%=mod;//加法原理
	}while (next_permutation(b,b+9));//枚举全排列
	printf("%lld",ans);
	return 0;
}

后续

sto初二除了我以外的所有人

猜你喜欢

转载自blog.csdn.net/sugar_free_mint/article/details/84993130