2025/7/16模拟赛

2025/7/16\(\mathbf{} \begin{Bmatrix} \frac{{\Large TEST} }{{\color{Yellow}\Large Record} }\mathbf{} {No.8} \end{Bmatrix}\times{}\) NeeDna

难度主观估计:\({\color{BLUE} t1} <{\color{BLUE} t5} <{\color{Purple} t3} ={\color{Purple} t2}<{\color{Drak Purple} t4}\)

P11431 [COCI 2024/2025 #2] 差异

题目描述

给定无限长的,周期长度为 \(n\) 的非负整数序列 \(a\) 的前 \(n\)\(a_1,a_2,\cdots,a_n\)

给定无限长的,周期长度为 \(m\) 的非负整数序列 \(b\) 的前 \(m\)\(b_1,b_2,\cdots,b_m\)

给定正整数 \(k\),求出 \(\displaystyle \left(\sum_{1\le i\le k} a_i\oplus b_i\right)\bmod \left(10^9+7\right)\)

这题难度在于代码写法和读题,我最开始写成了

\[\biggl(\sum^{k}_{i=1}\sum^{k}_{j=1}a_i \oplus b_j\biggr) \pmod {10^9+7} \]

解法:

下列题解保证 \(n\le m\)

首先根据普通的数学,我们想到每 \(lcm(n,m)\) 的答案是一样大的。

接下来思考对于 \(a\to b\) 每一个点的答案怎么求。回到上述结论,会发现其实对于 \(b\) 上每一个点每经过 \(\frac m {gcd(n,m)}\) 出现一次。

所以每 \(lcm(n,m)\) 对于每一个 \(a_i\) 的对应答案都形成了一个环。此时我们可以把大于 \(lcm(n,m)\) 的值求出来了。那么 \(k\to k\pmod {lcm(n,m)}\) 了,这个时候答案就不是环,而是环上的一部分点。这一部分我们可以处理出来对于每一个答案的起始点和终点,这个时候我们断环为链,前缀和一下就好了。

你会发现其实 $\oplus $ 这个操作是不能直接前缀和的,我们考虑拆位,这样就可以处理了,相同位上数字相同则没有贡献,反之就有。

细节较多,代码实现较为繁琐。精细实现的话,时间复杂度 \(\Theta((n+m)\log V)\)

code:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef long double ld;

const int N=2e5+10,mod=1e9+7;

int n,m;

ll a[N],b[N],k;

pair<int,int> id[N];

int cnt=0;

vector<int> G[N],Num[N];

vector<ll> num[N];

bool vis[N];

void dfs(int u,int i,int dep){
    if(vis[u]) return;
    num[i].push_back(b[u]);
    id[u]={i,dep};
    vis[u]=true;
    for(auto v:G[u]) dfs(v,i,dep+1);
}   

int get_ans(int pl){
    for(int i=1;i<=cnt;i++){
        int len=num[i].size();
        for(int j=0;j<len;j++)
            Num[i][j+1]=Num[i][j+1+len]=(num[i][j]>>pl)&1;
        for(int j=1;j<=2*len;j++)
            Num[i][j]+=Num[i][j-1];
    }
    ll tim=k/n,ret=k%n;
    ll res=0;
    for(int i=1;i<=n;i++){
        ll t=tim+(i<=ret?1:0);
        int cir=id[(i-1)%m+1].first,beg=id[(i-1)%m+1].second;
        int len=num[cir].size();
        ll tm=t/len,rest=t%len;
        if((a[i]>>pl)&1){
            res+=tm*(len-Num[cir][len]);
            res+=(rest-Num[cir][beg+rest-1]+Num[cir][beg-1]);
        }
        else{
            res+=tm*Num[cir][num[cir].size()];
            res+=Num[cir][beg+rest-1]-Num[cir][beg-1];
        }
    }
    return res%mod;
}

ll pw[65]={1};

int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n>>m>>k;
    for(int i=1;i<=64;i++) pw[i]=pw[i-1]*2%mod;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=m;i++){
        cin>>b[i];
        G[i].push_back((n+m+i-1)%m+1);
    }
    for(int i=1;i<=m;i++)
        if(!vis[i]) dfs(i,++cnt,1);
    for(int i=1;i<=cnt;i++)
        Num[i].resize(num[i].size()*2+10);
    ll ans=0;
    for(int i=0;i<=62;i++)
        (ans+=1ll*get_ans(i)*pw[i]%mod)%=mod;
    cout<<ans;
	return 0;
}

P11433 [COCI 2024/2025 #2] 三角

题目描述

给定一张 \(6n\) 个节点 \(m\) 条边的无向图。保证这张图可以被划分\(2n\)\(K_3\)(大小为 \(3\) 的完全图)。

求出这张图中的 \(n\)\(K_3\),不能有重复顶点。

解法

这道题做过考场上还写挂了,不像人

这道题显然可以乱搞,因为原图可以被划分\(2n\)\(K_3\),所以找到 \(n\)\(K_3\) 是比较容易的,我们直接随机化。

详情见代码。

code:

#include<bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int N=2e6+10;
int n,m,vis[N<<1],cnt,tim,a[N];
struct node{int u,v;}h[N<<1];
map<pair<int,int>,int> mp;
vector<int> g[N];
void solve(){
	for(int i=1;i<=n*6;i++) a[i]=i;
	while(1){
		tim++;cnt=0;
		random_shuffle(a+1,a+n*6+1);
		for(int i=1;i<=n*6;i++){
			if(vis[a[i]]==tim||vis[g[a[i]][1]]==tim||vis[g[a[i]][0]]==tim||mp[{g[a[i]][0],g[a[i]][1]}]==0) continue;
		    cnt++;vis[a[i]]=vis[g[a[i]][1]]=vis[g[a[i]][0]]=tim;
			if(cnt>=n) return;
		}
	}
}
signed main(){
	srand(time(0));
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int T;
	cin>>T;
	while(T--){
		cin>>n>>m;cnt=0;tim=0;
		memset(vis,0,sizeof(vis));
		memset(g,0,sizeof(g));mp.clear();
	    for(int i=1,u,v;i<=m;i++){cin>>u>>v;mp[{u,v}]=mp[{v,u}]=1;h[i].v=v;h[i].u=u;}
		random_shuffle(h+1,h+m+1);
    	for(int i=1;i<=m;i++){int u=h[i].u,v=h[i].v;g[u].pb(v);g[v].pb(u);}
		solve();
		tim++;cnt=0;
		for(int i=1;i<=n*6;i++){
			if(vis[a[i]]==tim||vis[g[a[i]][1]]==tim||vis[g[a[i]][0]]==tim||mp[{g[a[i]][0],g[a[i]][1]}]==0) continue;
			cout<<a[i]<<" "<<g[a[i]][1]<<" "<<g[a[i]][0]<<'\n';
			cnt++;vis[a[i]]=vis[g[a[i]][1]]=vis[g[a[i]][0]]=tim;
			if(cnt==n) break;
		}
	}
	return 0;
}

P11432 [COCI 2024/2025 #2] 流明

from huhangqi

直接处理并不方便,尝试找一些性质。

首先,我们需要关闭的是一段区间,而无论我们从这个区间左边右边还是中间开始处理,都是在左端点或者右端点结束。

由于关灯不需要时间,那么可以将这些区间简化为只存在左右端点的灯。

那么这样我们只需要处理这些端点的灯就可以了。

继续观察可以发现,只要两个端点的灯还亮着,那么我们只需要在关闭这两个灯时,把中间亮着的灯都关了就好了,所以我们只需要管目前剩余的左右两端的灯

那么我们就容易想到设置状态 \(dp_{l,r,0/1}\) 分别表示最左端的灯的位置和最右端的灯的位置以及自己目前在哪一端。

转移只需要选择去关掉左边还是关掉右边即可。

转移过后还需要看看想关掉的灯是否可以关了,如果不行,那么需要等待到这个时间,直接和关灯时间取较大值即可完成。

具体转移直接看代码即可。

但是这样空间不够,空间不够的 dp 题目最常见的就是滚动数组优化。

这道题目明显可以使用滚动数组优化,直接优化即可。

剩下的内容就是朴素区间 dp 了。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,cnt,dp[2][10005][2],ans;
struct P{
	int x,t;
}a[10005];
bool cmp(P a,P b){
	return a.x<b.x;
}
signed main(){
	cin>>n;
	for(int i=1,l,r,t;i<=n;i++){
		cin>>l>>r>>t;
		a[++cnt]={l,t},a[++cnt]={r,t};
	}
	sort(a+1,a+cnt+1,cmp);
	memset(dp,0x3f,sizeof(dp));
	ans=8e18;
	dp[0][1][0]=max(a[1].x,a[1].t);
	dp[0][1][1]=max(a[cnt].x,a[cnt].t);
	for(int len=cnt-1,id=0;len>=1;len--){
		id^=1;
		memset(dp[id],0x3f,sizeof(dp[id]));
		for(int l=1;l+len-1<=cnt;l++){
			int r=l+len-1;
			if(l>1){
				dp[id][l][0]=min(dp[id][l][0],dp[id^1][l-1][0]+a[l].x-a[l-1].x);
				dp[id][l][1]=min(dp[id][l][1],dp[id^1][l-1][0]+a[r].x-a[l-1].x);
			}
			if(r<cnt){
				dp[id][l][0]=min(dp[id][l][0],dp[id^1][l][1]+a[r+1].x-a[l].x);
				dp[id][l][1]=min(dp[id][l][1],dp[id^1][l][1]+a[r+1].x-a[r].x);
			}
			dp[id][l][0]=max(dp[id][l][0],a[l].t);
			dp[id][l][1]=max(dp[id][l][1],a[r].t);
			if(len==1)ans=min(ans,min(dp[id][l][0],dp[id][l][0]));
		}
	}
	cout<<ans;
	return 0;
}

t2和t4都是字符串,不会,所以先不写。

posted @ 2025-07-16 19:06  NeeDna  阅读(15)  评论(0)    收藏  举报