水题记录 1.6

水题

CF1887E

link

考虑对于建点,一个点 ( i , j ) (i,j) (i,j) 有数字 w w w 即为第 i i i 行的点 i i i 和第 j j j 列的点连了一条边权为 w w w 的边。

考虑到图是二分图,且 2 n 2n 2n 点, 2 n 2n 2n 边,必有偶环。

考虑找到一个偶环。

讨论环长是否为四的倍数。若不为四的倍数,则询问任意一对正对面的点对应的格子;若为四的倍数,错开一位即可。

考虑之后一定有一部分偶环,还是环上边权两两不同,最终缩到一个 4 4 4 条边的环即可。

void dfs(int u,int pa){
	if(ok)
		return ;
	vis[u]=1;
	s[++sid]=u;
	for(int v:E[u]){
		if(v==pa||vis[v]==2)
			continue;
		if(vis[v]==1&&!ok){
			if(v<=n)
				e.push_back(v);
			while(s[sid]!=v){
				e.push_back(s[sid]);
//				cout<<s[sid]<<" "<<sid<<"\n";
				s[sid--]=0;
			}
			if(v>n)
				e.push_back(v);
			ok=1;
			return ;
		}
		dfs(v,u);
	}
	s[sid--]=0;
	vis[u]=2;
}
void solve(){
	cin>>n;
	m=n<<1;
	for(int i=1;i<=m;i++)
		for(int j=1;j<=m;j++)
			col[i][j]=0;
	for(int i=1;i<=m;i++)
		E[i].clear();
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		y+=n;
		E[x].push_back(y);
		E[y].push_back(x);
		col[x][y]=col[y][x]=i;
	}
	fill(vis+1,vis+m+1,0);
	fill(s+1,s+m+1,0);
	e.clear();
	for(int i=1;i<=m;i++){
		if(!vis[i]){
			ok=sid=0;
			dfs(i,0);
			if(ok)
				break;
		}
	}
//	cout<<e.size()<<"\n";
	while(e.size()>4){
		int sz=e.size();
		int l=0,r=(sz%4==0?sz/2+1:sz/2);
		cout<<"? "<<e[l]<<" "<<e[r]-n<<"\n";
		cout.flush();
		int c;
		cin>>c;
		col[e[l]][e[r]]=col[e[r]][e[l]]=c;
		int res=0;
		for(int i=l;i<r;i++)
			if(col[e[i]][e[i+1]]==c)
				res=1;
		vector<int>e1;
		e1.clear();
		e1.push_back(e[l]);
		e1.push_back(e[r]);
		if(res==1)
			for(int i=r+1;i<sz;i++)
				e1.push_back(e[i]);
		else
			for(int i=r-1;i>=1;i--)
				e1.push_back(e[i]);
		swap(e,e1);
	}
	cout<<"! "<<e[0]<<" "<<e[2]<<" "<<e[1]-n<<" "<<e[3]-n<<"\n";
	cout.flush();
	string ok;
	cin>>ok;
	if(ok=="OK")
		return ;
	while(1);
}

CF453B

lnik

考虑到,所有 b i < 59 b_i<59 bi<59,小于 59 59 59 16 16 16 个质数。

考虑 f [ i , S ] f[i,S] f[i,S] 表示前 i i i 数,后面不可使用的质数集合为 S S S

考虑转移,记 p [ i ] p[i] p[i] i i i 的质数集合,则 f [ i ] [ S ] = min ⁡ f [ i − 1 ] [ p [ k ] ] + ∣ k − a [ i ] ∣ f[i][S]=\min f[i-1][p[k]]+|k-a[i]| f[i][S]=minf[i1][p[k]]+ka[i],转移条件为现在 S S S 包含 p [ k ] p[k] p[k]

void solve(){
	cin>>n;
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		for(int j=0;j<1<<M;j++){
			f[i][j]=1e9;
			for(int k=1;k<59;k++){
				if((j&prs[k])==prs[k]){
					int nx=f[i-1][j^prs[k]]+abs(x-k);
					if(nx<f[i][j])
						f[i][j]=nx,p[i][j]=k;
				}
			}
		}
	}
	int no=(1<<M)-1;
	for(int i=n;i>=1;i--){
		ans[i]=p[i][no];
		no^=prs[p[i][no]];
	}
	for(int i=1;i<=n;i++)
		cout<<ans[i]<<" ";
}

CF1783D

link

DP

考虑 f [ i , j ] f[i,j] f[i,j] 表示考虑到 i i i 操作,此时 a [ i + 1 ] a[i+1] a[i+1] j j j 的方案数。

转移时考虑去重当前位为 0 0 0 的情况。

void solve(){
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	int v=300*n;
	f[1][a[2]+v]=1;
	for(int i=2;i<n;i++){
		for(int j=-v;j<=v;j++){
			f[i][a[i+1]+j+v]+=f[i-1][j+v];
			f[i][a[i+1]-j+v]+=f[i-1][j+v];
			if(!j) f[i][a[i+1]-j+v]-=f[i-1][j+v];
			f[i][a[i+1]+j+v]%=mod;
			f[i][a[i+1]-j+v]%=mod;
		}
	}
	int ans=0;
	for(int i=0;i<=2*v;i++)
		ans+=f[n-1][i],ans%=mod;
	cout<<ans<<"\n";
}

CF279D

link

DP

f [ i ] [ S ] f[i][S] f[i][S] 表示拼出了 a i a_i ai,当前保存的数集合为 S S S 时最小的 m m m

考虑滚动数组。

考虑优化,发现转移为在这里插入图片描述

意味着只用转移一次即可。

那么就做完了,时间复杂度 O ( n 2 n ) O(n2^n) O(n2n)

for(int i=0;i<n;i++)
		cin>>a[i];
	memset(f,0x3f3f3f3f,sizeof f);
	f[0][1]=1;
	for(int i=1;i<n;i++){
		int np=i&1,pp=np^1;
		memset(f[np],0x3f3f3f3f,sizeof f[np]);
		memset(vis,0,sizeof vis);
		vector<pair<int,int> >D;
		D.clear();
		for(int j=0;j<i;j++)
			for(int k=j;k<i;k++){
				if(a[j]+a[k]==a[i])
					D.push_back({j,k});
			}
		if(D.empty()){
			cout<<-1<<"\n";
			return 0;
		}
		for(auto [x,y]:D){
			for(int S=0;S<1<<i;S++){
				if(vis[S])
					continue;
				if((S>>x)&1)if((S>>y)&1){
					vis[S]=1;
					f[np][S|(1<<i)]=min(f[np][S|(1<<i)],f[pp][S]+1);
					for(int j=0;j<i;j++)
						if((S>>j)&1)
							f[np][(S^(1<<j))|(1<<i)]=min(f[np][(S^(1<<j))|(1<<i)],f[pp][S]);
				}
			}
		}
	}
	int ans=0x3f3f3f3f;
	for(int i=0;i<1<<n;i++)
		ans=min(ans,f[(n-1)&1][i]);

CF1368E

link

在这里插入图片描述

考虑如此删除,发现删除点数不超过 4 7 n \frac{4}{7} n 74n

我们以此给节点标记度数,删掉了就清零。

那么一旦某个点历史最大度数达到 3 3 3 就要删除了。

queue<int>Q;
	for(int i=1;i<=n;i++)
		if(!deg[i])
			d[i]=1,Q.push(i);
	while(!Q.empty()){
		int u=Q.front();Q.pop();
		for(int v:E[u]){
			deg[v]--;
			if(!deg[v])
				Q.push(v);
			if(vis[v])
				continue;
			d[v]=max(d[u]+1,d[v]);
			if(d[v]>=3)
				ans.push_back(v),d[v]=0,cnt++,vis[v]=1;
			
		}
	}

luogu6583

link

考虑计数 i j d j \frac{ij}{dj} djij,其中 d = 2 a 5 b , j d=2^a5^b,j d=2a5b,j 不包含 2 , 5 2,5 2,5

f ( n ) f(n) f(n) n n n 以内只含 2 , 5 2,5 2,5 的数个数,这个可以在 O ( log ⁡ 2 n log ⁡ 5 n ) O(\log_2n\log_5n) O(log2nlog5n) 时间内解决。

枚举 j j j,贡献为 n j × f ( n j ) \frac{n}{j}\times f(\frac{n}{j}) jn×f(jn)

发现这个形式可以整除分块。

但是需要统计一段区间内不含 2 , 5 2,5 2,5 的数的个数,这个可以容斥简单做。

inline int ga(int x){
	return x-x/2-x/5+x/10;
}
inline int calc(int x){
	int res=0,o=lg2(x);
	for(int i=0;i<=o;i++){
		res+=lg5(x>>i)+1;
	}
	return res;
}
void solve(){
	cin>>n;
	int ans=0;
	for(int l=1,r;l<=n;l=r+1){
		r=(n/(n/l));
		int base=ga(r)-ga(l-1);
		int v=n/l;
		ans+=base*v*calc(v);
	}
	cout<<ans<<"\n";
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值