嘉然!为了你,我要写题解!Codeforces #738(Div.2)

嘉然!为了你,我要写题解!Codeforces Round #738(Div.2)

一个是刚复习完莫比乌斯来写了E题,顺便补了这场的D题(https://codeforces.com/contest/1559/problem/D2),感觉挺妙的,写个题解捏。

因为是赛后补题,有时间看题目背景,于是:

Mocha and Diana are friends in Zhijiang, both of them have a forest with nodes numbered from 1 to n, and they would like to add edges to their forests such that:

我直接补完所有题还写个题解

🤤🤤嘿嘿🤤🤤然然🤤🤤嘿嘿嘿🤤我的然然🤤🤤然然然然🤤🤤🤤我的然然

A. Mocha and Math

https://codeforces.com/contest/1559/problem/A

因为说了“This operation can be performed any number of times.”,那就好办了,只要我们想,我们总有办法让所有数变成a1&a2&&an,而AND运算一直做下去肯定不会让答案变大,只有可能变小,所以答案就是把所有aiAND起来。

cin>>T;
rep(tc,1,T){
cin>>n;rep(i,1,n)cin>>a[i];
ll ret=a[1];
rep(i,2,n)ret&=a[i];
cout<<ret<<endl;
}

B.Mocha and Red and Blue

https://codeforces.com/contest/1559/problem/B

一个只含BR和?的序列,给?填上B或者R,使得BB或者RR尽可能少。换言之就是要尽可能多的BR,考虑每一段连续的X???X,其实中间问号怎么填是已经确定下来的:比如如果是B???...X,就一定是填成BRBR...X,就算?X后面变成RR也没办法,换成B开头也一样会少一个贡献。

所以其实如果从前往后扫一遍就能确定下来所有左边有东西的???段,然后我再判一下开头就是???X的情况:

char str[N],ch[]="BR";
vector<pii> seg;
void solve()
{
seg.clear();
for(int i=1;i<=n;i++)if(str[i]=='?')
{
int st=i;
while(i+1<=n&&str[i+1]=='?')i++;
seg.pb(mp(st,i));
}
for(auto itr:seg)
{
if(itr.fi==1)
{
for(int i=itr.se,t=(str[itr.se+1]=='B');i>=1;i--,t^=1)
str[i]=ch[t];
}else
{
for(int i=itr.fi,t=(str[itr.fi-1]=='B');i<=itr.se;i++,t^=1)
str[i]=ch[t];
}
}
}
int main()
{
scanf("%d",&T);
rep(tc,1,T)
{
scanf("%d",&n);
scanf("%s",str+1);
solve();
printf("%s\n",str+1);
}
return 0;
}

C. Mocha and Hiking

https://codeforces.com/contest/1559/problem/C

The city where Mocha lives in is called Zhijiang. There are n+1 villages and 2n−1 directed roads in this city.

🤤这下到枝江了捏。

画一下图,发现如果有01这种结构的出现,那就走1i(n+1)i+1n这条路线,否则一定是形如1110000的结构,如果有1就从(n+1)出发走一遍,否则全是0就从1开始走,最后走n+1。综上,符合条件的路径一定存在。

D. Mocha and Diana

https://codeforces.com/contest/1559/problem/D2

大的来了捏

Mocha and Diana are friends in Zhijiang, both of them have a forest with nodes numbered from 1 to n

, and they would like to add edges to their forests such that:

  • After adding edges, both of their graphs are still forests.
  • They add the same edges. That is, if an edge (u,v)

is added to Mocha's forest, then an edge (u,v)

  • is added to Diana's forest, and vice versa.

Mocha and Diana want to know the maximum number of edges they can add, and which edges to add.

D1和D2合起来写了,两张图,保证都是森林,要给(u,v)加边就要同时加边,尽可能多地加一些边使得最后的图还是森林,给出加边的方案。类似的问题似乎之前在组队训练的时候见过一次(不过那题应该比较难的感觉),关键是注意到一些性质:首先说是森林,其实当连通块考虑就行,不用考虑树的具体形态。以及进一步地,连边也可以看成两个连通块连接。

这样这就引出了一个关键的信息:最后连完边一定有一张图只有一个连通块,否则就一定可以继续连边了对吧。进一步因为只有一个连通块,所以连边顺序也无所谓了,于是对于n比较小的D1题就可以n方地过掉了。

而对于D2,依然是用前面的性质往下想,既然连边顺序无所谓,那一开始就干脆全和1连:记两张图分别为G0,G1bl[x]x所在的连通块,对于2,3,,n这些点,如果bl[i]G0,G1都和1不连通,那就直接连上(1,i),否则如果bl[i]G0和1连通,在G1和1不连通,以及有一个bl[j]G0和1不连通,而在G1和1连通,那bl[i]bl[j]其实是可以连边的。

于是只要先把第一种情况的连起来,再O(nlogn)地处理一下剩下的点(因为是连通块,bl[i]有可能有重复的,去个重捏)

rep(i,2,n)
if(find(0,1)!=find(0,i)&&find(1,1)!=find(1,i))
{
addEdge(0,1,i);addEdge(1,1,i);
ret_edge.pb(mp(1,i));
}
rep(i,2,n)
{
if(find(0,1)!=find(0,i))s1.insert(find(0,i));
if(find(1,1)!=find(1,i))s2.insert(find(1,i));
}
for(auto a=s1.begin(),b=s2.begin();a!=s1.end()&&b!=s2.end();a++,b++)
ret_edge.pb(mp(*a,*b));
cout<<ret_edge.size()<<endl;
for(auto itr:ret_edge)
cout<<itr.fi<<' '<<itr.se<<endl;

E. Mocha and Stars

https://codeforces.com/contest/1559/problem/E

如果没有gcd=1的约束就是一个比较裸的背包:f[i][j]表示容量j的背包装前i个物品的方案数,f[i][j]=k=jr[i]jl[i]f[i1][k]这样子,加上前缀和优化就可以O(nM)地求捏。

加上gcd这个约束之后,很容易让人想到莫比乌斯对吧(でしょう~)

先暴力地把答案写成

a1=l1r1an=lnrn[(a1,,an)=1][a1++anm]=a1=l1r1and|(a1,,an)μ(d)[a1++anm]

然后就是改写a1,,an,把d放到前面:
d=1mμ(d)a1=l1/dr1/dan[a1++anmd]

然后发现后面的问题变成了上面的背包,以及这个背包是O(nmd)的,调和级数,求和之后就是O(nMlogM)的。

于是就做完了捏~:

void init()
{
mu[1]=1;
rep(i,2,M-5)
{
if(!not_pri[i])
{
pri_list.pb(i);
mu[i]=-1;
}
for(auto j:pri_list)
{
if(1ll*i*j>M-5)break;
not_pri[i*j]=1;
if(i%j==0){mu[i*j]=0;break;}
mu[i*j]=-mu[i];
}
}
}
ll calc(int d,int mx)
{
rep(i,0,mx)f[0][i]=1;
rep(i,0,mx)s[0][i]=i+1;
rep(i,1,n)
{
int L=(l[i]+d-1)/d,R=r[i]/d;
if(L>R)return 0;
rep(j,0,mx)
{
f[i][j]=((j-L>=0?s[i-1][j-L]:0)-(j-R-1>=0?s[i-1][j-R-1]:0)+MOD)%MOD;
s[i][j]=((j?s[i][j-1]:0)+f[i][j])%MOD;
}
}
return f[n][mx];
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
init();
cin>>n>>m;
rep(i,1,n)cin>>l[i]>>r[i];
ll ret=0;
rep(d,1,m)ret=(ret+1ll*mu[d]*calc(d,m/d)%MOD+MOD)%MOD;
cout<<ret;
return 0;
}
posted @   yoshinow2001  阅读(178)  评论(4编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示