首页 > 其他分享 >Codeforces Round 902 Div 1 (CF 1876)

Codeforces Round 902 Div 1 (CF 1876)

时间:2023-10-10 14:33:08浏览次数:45  
标签:902 颜色 int 线段 Codeforces CF fa 序列 下标

A. Helmets in Night Light

按花费 sort 一下,\(b<p\) 就让他用 \(b\) 的花费告诉别人,剩下的人一开始用 \(p\) 的花费告诉即可。

B. Effects of Anti Pimples

发现一个数会被所有它的因数贡献,\(O(n\sqrt{n})\) 随便算一算,式子略。

C. Autosynthesis

Solution 1

想到了建图但没有完全想到,于是有了这个神秘做法。

题意等价于选择一个 下标集合,使它和 它补集对应的值域集合 相等。设它们分别为 \(S,T\)。
由于不同位置的 \(a_i\) 有可能相等,设 \(|S|\) 表示 \(S\) 的元素个数,则有 \(|T|\le |S|\)。

考虑 \(|S|>|T|\) 的情况,则一定至少存在一个下标 \(i\in [1,n]\),\(i\) 没在值域集合里出现过。那么如果在 \(S\) 集合选择下标 \(i\),没法找到一个数在 \(T\) 集合里与它对应。根据这一点我们可以判断哪些下标一定不选。
对于一定不选的下标 \(i\),则根据补集的定义值为 \(a_i\) 的数一定在 \(T\) 中被选,同时下标为 \(a_{a_i}\) 的数一定在 \(S\) 中被选。以此类推,只要还满足 \(|S|>|T|\) 我们就可以一直把已经确定选不选的数从集合里去掉,直到 \(|S|=|T|\)。

考虑 \(|S|=|T|\) 的情况,这等价于 \(T\) 是一个 \(1\sim n\) 的排列。如果把值 \(a_i\) 选进 \(T\),下标 \(a_i\) 就必须在 \(S\)。限制等价于下标 \(i\) 和 \(a_i\) 只能选一个,连边 \(i\to a_i\),判图上有没有奇环即可。

Solution 2

一开始就考虑按 \(i\to a_i\) 的方式建图,那么每个点有一条出边,图是一个内向基环树森林。

分类讨论下标 \(i\) 选不选:

  • 不选点 \(i\),则点 \(a_i\) 一定被选。
  • 选点 \(i\),则至少有一个 \(a_j=i\) 的下标 \(a_j\) 不被选。

只考虑树上的答案,限制就变成在树上选择一些点,每个点要么儿子和父亲全被选,自己不选;要么儿子不全被选,自己被选。

显然所有叶子都不能选,从叶子向根依次考虑答案即可。然后图上就只剩下了若干个环,回到了 Solution 1 的第二种情况。

代码是 Solution 1。

Code
const int N=2e5+5;
int flag[N],a[N],n,cnt[N];
queue<int> q;
void dfs(int u)
{
	if(flag[a[u]]==flag[u]) {printf("-1\n");exit(0);}
	else if(flag[a[u]]!=-1) return;
	flag[a[u]]=flag[u]^1;
	dfs(a[u]);
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++) a[i]=read(),cnt[a[i]]++;
	for(int i=1;i<=n;i++) flag[i]=-1;
	for(int i=1;i<=n;i++) if(!cnt[i]) q.push(i);
	while(!q.empty())
	{
		int u=q.front(); q.pop(),flag[u]=0;
		if(flag[a[u]]==0) {printf("-1\n");return 0;}
		int lst=flag[a[u]]; flag[a[u]]=1;
		if(lst==-1)
		{
			cnt[a[a[u]]]--;
			if(!cnt[a[a[u]]]) q.push(a[a[u]]);
		}
	}
	for(int i=1;i<=n;i++) if(flag[i]==-1) flag[i]=1,dfs(i); 
	int qwq=0;
	for(int i=1;i<=n;i++) if(!flag[i]) qwq++;
	printf("%d\n",qwq);
	for(int i=1;i<=n;i++) if(!flag[i]) printf("%d ",a[i]); 
	cout<<endl; return 0;
}

D. Lexichromatography

被诈骗了 & 不能觉得不会下分就过完 C 摆烂啊!

Description

给定长度为 \(n\) 的序列 \(a\),要求将序列每个位置染成红色或蓝色,且满足以下条件:

  • 将染成红色和蓝色的数分别拼成两个序列,蓝色序列的字典序小于红色;
  • \(a\) 的每个子段都满足对于子段内任意的 \(x\),\(a_i=x\) 的所有位置 \(i\) 染成红色和蓝色的数量相差不超过 \(1\)。

求不同的染色方案数,答案对 \(998\,244\,353\) 取模。\(n,a_i\le 2\times 10^5\)。

Solution

有字典序大小的限制是不好计算答案的,但是这条性质是诈骗。对于任意一种两序列不相等的染色方案,我们把红蓝对调一下,发现这两种方案里一定恰好有一个满足字典序的限制。

也就是说在满足第二条限制的情况下,设总染色方案数为 \(All\),两序列相等的方案数为 \(cnt\),则 \(ans=\frac{All-cnt}{2}\)。

第二条限制实际就是说我们对于每个 \(x\),把 \(a_i=x\) 的位置取出来拼成一个序列,序列里相邻的两个数颜色相反。令不同的 \(a_i\) 为 \(col\),对于每种 \(a_i\) 确定第一个的颜色就可以确定所有,即 \(All=2^{col}\)。

考虑如何求红蓝序列相同的方案数。

首先如果某种 \(a_i\) 出现了奇数次就直接寄了,否则我们把 \(a_i\) 相同的位置从左到右两两分组形成一些线段,限制是每条线段的左右端点不同色。

分类讨论线段 \([a,b]\) 和 \([c,d]\) 的位置关系。
如果两条线段没有交集,它们怎么染色互不影响;如果有包含关系,怎么染色都不能让序列相等,令 \(cnt=0\)。
否则就是它们有交集的情况,设 \(a<c<b<d\)。那么 \(a,c\) 一定同色,\(b,d\) 一定同色。

发现我们的限制的形式都是某两点颜色相同 / 相反,使用形如食物链一题的扩展域并查集维护。那么设连通块个数为 \(block\),则 \(cnt=2^{block}\)。

实现上,我们对每个线段不用向所有与它有交的线段连边。因为两个与它有交的线段之间也有交,它们一定之前已经连通。所以在其中随便找一个连即可,这样时间复杂度就对了,为 \(O(n\log n)\)。

Code
#define int long long 
const int N=4e5+5,mod=998244353;
const int inv2=((mod+1)>>1);
int n,a[N];
int lst[N],tot[N],fa[N];
int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);}
void merge(int x,int y)
{
    if(find(x)==find(y)) return;
    fa[find(x)]=find(y);
}
struct node{int l,r;} e[N];
vector<int> t[N];
int cnt,all=1,ans=1,b[N];
signed main()
{
    n=read();
    for(int i=1;i<=n;i++) a[i]=b[i]=read();
    sort(b+1,b+n+1);
    for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+n+1,a[i])-b;
    for(int i=1;i<=(n<<1);i++) fa[i]=i;
    for(int i=1;i<=n;i++)
    {
        if(lst[a[i]])
        {
            merge(lst[a[i]],i+n),merge(lst[a[i]]+n,i);
            if(tot[a[i]]&1) e[++cnt]={lst[a[i]],i};
        }
        tot[a[i]]++,lst[a[i]]=i;
    }
    for(int i=1;i<=cnt;i++) t[e[i].l].push_back(e[i].r);
    for(int i=1;i<=n;i++) if(tot[i]) all=all*2%mod;
    for(int i=1;i<=n;i++) if(tot[i]&1) {printf("%lld\n",all*inv2%mod);return 0;}
    set<int> s;
    for(int i=1;i<=n;i++)
    {
        for(auto j:t[i])    
        {
            if(s.upper_bound(j)!=s.end()) {printf("%lld\n",all*inv2%mod);return 0;}
            if(!s.empty())
            {
                int r=*s.begin();
                merge(j,r),merge(j+n,r+n);
            }
            s.insert(j);
        }
        s.erase(i);
    }
    for(int i=1;i<=n;i++) if(find(i)==i) ans=(ans<<1)%mod;
    ans=(all-ans+mod)%mod*((mod+1)/2)%mod;
    printf("%lld\n",ans);
    return 0;
}

E. Ball-Stackable

Description

给你一棵 \(n\) 个点的树,其中有一些边方向给定,剩下的边由你来决定方向。同时你要给每条边染一个颜色。

现在有一个人在树上走,他有一个栈,当他经过一条边时会进行如下操作:

  • 如果他走的方向与边的方向相同,往栈里放一个与该边颜色相同的球。
  • 如果他走的方向与边的方向相反,从栈顶取出一个球丢掉。

一个路径合法当且仅当每次走相反的边之前栈都不为空。

你构造的方案要满足对于任意合法路径,每次走反向边时取出的球都恰好与该边颜色相同。在此基础上使边的颜色数最多,并输出方案,无解输出 -1

\(n\le 10^5\)。

Solution

首先让所有边都是一个颜色肯定是符合条件的,不可能无解。

考虑什么样的东西会对总颜色数产生限制,手玩样例可以发现如果一个点有很多条入边,那么这些入边的颜色必须都相同(否则从一个走向另一个就会没有对应颜色的球)。我们定向的时候希望这样的点尽量少。

知道这一点,如果没有已经定向的边就好做了,我们选一个根把树定向成外向树,那么没有点入度大于 \(1\),颜色数可以达到 \(n-1\)。

对于有定向边的情况,假设已经确定了根,那么我们把未定向的边按外向树方向定向。这时树中会出现一些反向边。
那么对于任意从根出发的路径,走一个反向边之前栈顶的球颜色都是确定的,维护从根直着走下来的栈顶颜色,把它设为该颜色即可。证明很简单,因为如果中间走过别的子树肯定是每条边正反走两遍,贡献会在出子树时都退掉。

使用换根 dp 求出每个点作为根的答案即可。

对于每次栈不能为空的限制,我们考虑如果当前根是 \(rt\),不合法的点是 \(x\)。不合法代表这条路径上反向边比正向边多,那么把根换成 \(x\) 只会取反 \(rt\to x\) 路径上的所有边,一定可以使路径合法,且答案更优。所以我们找到的最优答案所在的根一定满足栈不为空的限制条件。

Code
const int N=1e5+5;
int n;
struct edge{int nxt,to,w;} e[N<<1];
int head[N],cnt,col;
il void add(int u,int v,int w) {e[++cnt]={head[u],v,w};head[u]=cnt;}
int f[N],mx,rt;
void dfs1(int u,int fa)
{
    for(int i=head[u];i;i=e[i].nxt)
        if(e[i].to^fa) f[1]+=(e[i].w!=-1),dfs1(e[i].to,u);
}
void dfs2(int u,int fa)
{
    if(f[u]>mx) mx=f[u],rt=u;
    for(int i=head[u];i;i=e[i].nxt)
        if(e[i].to^fa) f[e[i].to]=f[u]-e[i].w,dfs2(e[i].to,u);
}
vector<int> q;
void dfs3(int u,int fa)
{
    for(int i=head[u];i;i=e[i].nxt)
    {
        int v=e[i].to,c=0; if(v==fa) continue;
        if(e[i].w==-1) printf("%d %d %d\n",v,u,c=q[q.size()-1]),q.pop_back();
        else printf("%d %d %d\n",u,v,++col),q.push_back(col);
        dfs3(v,u);
        if(e[i].w==-1) q.push_back(c); else q.pop_back();
    }
}
int main()
{
    n=read();
    for(int i=1;i<n;i++)
    {
        int u=read(),v=read(),w=read();
        add(u,v,w),add(v,u,-w);
    }
    dfs1(1,0),dfs2(1,0); printf("%d\n",mx);
    dfs3(rt,0);
    return 0;
}

F/G

没有官方题解。没有洛谷题解。咕。

标签:902,颜色,int,线段,Codeforces,CF,fa,序列,下标
From: https://www.cnblogs.com/ying-xue/p/17754087.html

相关文章

  • Educational Codeforces Round 156 (Rated for Div. 2)
    EducationalCodeforcesRound156(RatedforDiv.2)A.SumofThree解题思路:如果\(n\leq6或n=9\),无解。若\(n\%3==0,t=\lfloor\frac{3}{n}\rfloor\):若\(t=0\)构造\(1,4,n-5\)否则,构造\(t-3,t,t+3\)若\(n\%3==1:构造1,2,n-3\)若\(n......
  • Codeforces Round 902 (Div. 2, based on COMPFEST 15 - Final Round)
    目录写在前面ABCDE写在最后写在前面比赛地址:https://codeforces.com/contest/1877。呜呜铃果唱歌太好听了、、、我宣布是第二喜欢的声线,第三喜欢是东北切蒲英,第一喜欢绝赞招募中。这下不得不成为数码推了、、、A答案为\(-\suma_i\)。懒得写代数式子推了,赛时看完题直接......
  • Educational Codeforces Round 152 (Div. 2) D. Array Painting(双指针)
    EducationalCodeforcesRound152(Div.2)D.ArrayPainting//思路:双指针找连续正数段//若段中出现2,则更新两头的0的情况,若为涂色则改为true//若无2,则优先更新左侧0,若左0已经为true,则更新右侧0//数组开头结尾特判#defineintlonglong#defineldlongdoubleusingnam......
  • 练习记录-cf-Educational Codeforces Round 156 (Rated for Div. 2)(A-C)
    好久没打了还是就出了三道不过还好没掉分A.SumofThree就是问能不能把一个数拆成三个不同的且都不能被三整除的数我的思路就是拆成1+2+一个大于等于4的数如果拆了后另一个数是%3==0那么我拆成1+4它肯定就不被整除然后判下相同#include<bits/stdc++.h>#defineclose......
  • Codeforces Round 902 (Div. 2) C. Joyboard 规律
    CodeforcesRound902(Div.2)C.Joyboard//思路:在k=1,k=2,k=3时有解//当k=1时为全0//当k=2时,若m>=n,则先是0然后为1~n,最后一位可以为n的倍数也符合,即n+m/n-1//若m<n则为1~m即m//当k=3时,只能在n+1位是第3个不同情况(大于n),且不能为n的倍数,即(m-n)-(m/n-1)//只......
  • [902] Get the current file's directory of CMD batch scripts
    Inabatchfile,youcanusethe%~dp0specialvariabletogetthedirectoryofthecurrentlyexecutingbatchfile.Here'showyoucandoit:@echooffechoThedirectoryofthisbatchfileis:%~dp0Whenyourunthisbatchfile,itwilldisplaythe......
  • CF1877C Joyboard
    思路一个比较明显的结论是,不同的数字个数只可能是\(1,2,3\)。可以随手写一个暴力的输出程序,假定\(n\)和\(m\),把所有可能的序列都输出来,就可以发现这个规律。也可以感性思考一下。如果第\(n+1\)位是\(0\),那么整个序列都会是\(0\),个数也就是\(1\)。如果第\(n+1\)位......
  • 【做题笔记】CF 1400-1600 构造题
    本人比较菜,所以做的rating很低/kk/kk/kk欢迎各位大佬嘲讽这个蒟蒻/kk/kk/kk/kk$*$表示看了题解才过的(所以你会发现这里的大部分题后面都会有$*$)实时通过率直接贴在后面当不看题解通过率稳定在\(50\%\)以上就弃坑。希望早日弃坑ABBCorBACB*题面中给了两种操作......
  • CF986C AND Graph
    出题人纯nt要用bitset存bool数组来卡空间也真是没谁了这题的思路其实有点像高维前缀和,考虑对于某个数\(x\),我们知道\(y=(2^n-1)\oplusx\)与\(x\)的与一定为\(0\),且\(y\)的所有子集也满足与\(x\)后为\(0\)考虑怎么处理这种子集关系,我们借鉴于高维前缀和,每次把某个数\(y\)的某一......
  • CF723E One-Way Reform
    很有意思的一个题,刚开始想复杂了后面看了题解才发现是个傻逼题首先不难发现答案的上界数就是度数为偶数的节点数,考虑一种构造方法能打到这个上界不妨新建一个虚拟节点,将所有度数为奇数的点与其连边,这样图中所有点度数都变成了偶数,包括这个虚拟节点而对于一个所有点度数均为偶数......