构造杂题(ing)

「CF388B」Fox and Minimal path

构造一个图,使得第\(i\)层下方的点的路径数为\(2^i\),然后构造一条主链,通过二进制拆分,将对应位置与主链项链即可

void solve(){
	add(1,++idx);
	add(1,++idx);
	for(int i=1;i<l;i++){
		idx++;
		add(idx-1,idx);
		add(idx-2,idx);
		idx++;
		add(idx-2,idx);
		add(idx-3,idx); 
	}
	add(++idx,1); 
	for(int i=1;i<l;i++){
		idx++; 
		add(idx-1,idx); 
	}
	add(idx,2);
	for(int i=1;i<=l;i++){
		if(a[i]){//二进制倒数第i位
			if(2*(l+1)+i+1>idx) add((i+1)*2,2);
			else add((i+1)*2,2*(l+1)+i+1);
		}
	}
}

「CF1305D」Kuroni and the Celebration

询问叶子节点的lca,如若是ta们两个之一,那么ta(lca)为根

若不是,将\(x \to lca\)\(lca \to y\)这两条链上的点(处理)从set弹出(ta们不可能为根)

然后将\(lca\)加入下一轮的set中

重复此操作,直到set里只剩1个元素,ta即为根

void dfs(int u,int fa,int x){
	now.erase(u);
	if(u==x) return;
	for(int v:G[u]) {
		if(v!=fa) dfs(v,u,x);
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1,u,v;i<n;i++){
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
		d[u]++;d[v]++;
	}
	for(int i=1;i<=n;i++) {
		if(d[i]==1) now.insert(i);
	}
	while(now.size()>1){
		nxt.clear();
		while(1){
			if(now.size()==1) {
				nxt.insert(*now.begin());
				break;
			}else if(now.empty()) break;
			int x=*now.begin();
			now.erase(now.begin());
			int y=*now.begin();
			now.erase(now.begin());
			int lca;
			printf("? %d %d\n",x,y);
			fflush(stdout);
			scanf("%d",&lca);
			dfs(x,0,lca);
			dfs(y,0,lca);
			nxt.insert(lca);
		}
		now=nxt;
	}
	printf("! %d\n",*now.begin());
	fflush(stdout);
	return 0;
}

「CF1686D」Linguistics

我们将字符串按照相邻字符是否相同划分为多个段

对于只有A/B的段就用单独的a/b就🆗了

主要是对于AB相间的段怎么处理:

首先,对于奇数段:\(AB\),\(BA\)共用\(len/2\)

对于A开头的段:

  • 若只用\(AB\),那么用的是\(c:(len/2)\)

  • 若不是,则\(AB\),\(BA\),共用\((len/2)-1\)

对于B开头的段:

  • 若只用\(BA\),那么用的是\(c:(len/2)\)

  • 若不是,则\(AB\),\(BA\),共用\((len/2)-1\)

那么我们可以将这些段按照花费从小到大排序

然后若数量不够,则将sum加上\(w-1\),表示\(AB\),\(BA\)共用\(w-1\)

w=len/2,即花费

最后将c+d与sum比较,看c+d够不够即可

int main(){
	cin>>t;
	while(t--){
		scanf("%d%d%d%d",&a,&b,&ab,&ba);
		scanf("%s",s+1);
		len=strlen(s+1);
		cnta=0,cntb=0;
		for(int i=1;i<=len;i++){
			if(s[i]=='A') cnta++;
			else cntb++; 
		}
		if(cnta!=a+ab+ba||cntb!=b+ab+ba){
			printf("NO\n");
			continue;
		}
		sum=0;
		for(int i=1,j;i<=len;i=j+1){
			j=i;
			while(j<len&&s[j]!=s[j+1]) j++;
			if((j-i+1)%2==1) sum+=(j-i+1)/2;
			else{
				int w=(j-i+1)/2;
				if(s[i]=='A') x.push_back(w);
				else y.push_back(w);
			}
		}
		sort(x.begin(),x.end());
		sort(y.begin(),y.end());
		for(int i:x){
			if(ab>=i) ab-=i;
			else sum+=i-1;//i-1个ba 
		}
		for(int i:y){
			if(ba>=i) ba-=i;
			else sum+=i-1;//i-1个ab 
		}
		if(sum<ab+ba) printf("NO\n");
		else printf("YES\n");
	}
	return 0;
}

「CF1375E」 Inversion SwapSort

这个题就是我们先sort出我们的最终数组

然后将ta们的原id反排序回去(冒泡)

也就是倒着排回去,然后每次交换的时候记录下是交换的哪两个即可

这样就保证了每个逆序对只交换了一次

int main(){
    cin>>n;
    for (int i=1; i<=n; i++){
    	cin>>a[i].first;
		a[i].second=i;
    }
    sort(a+1,a+1+n);
    for (int i=1; i<=n; i++){
    	b[i]=a[i].second;
    }
    for (int i=1; i<=n; i++) {
    	for (int j=1; j<n; j++) {
    		if (b[j]>b[j+1]) {
                ans.push_back({b[j+1],b[j]});
                swap(b[j],b[j+1]);
            }
    	}
    }//冒泡排序
	cout<<ans.size()<<"\n";
	for(int i=0;i<(int)ans.size();i++){
		cout<<ans[i].first<<" "<<ans[i].second<<"\n";
	}
    return 0;
}

「CF1305E」 Kuroni and the Score Distribution

我们可以显然发现\(1,2,3,4,5,6,\dots ,n-1,n\)这样的数列满足条件的三元组的个数增长是最快的,所以我们不妨从此入手

\(i\)个数的贡献为\((i-1)/2\)

我们可以一直列下去,直到\(k\)(满足条件的三元组的个数)>=m

此时我们应该考虑如何在现有基础上将k缩小为m

我们可以比较不是很显然地发现,n(最后一项的数值)每增加2,k就减少1,

因此我们可以通过增大后面的数的数值来缩小k

使k=m即可

由于有n的限制,我们将后面的数随便填一些大的数(ta们的间隔也要大),

那么这个题就做完了

int main(){
	scanf("%lld%lld",&n,&m);
	for (ll i=1;i<=n;i++){
		a[i]=i;
		czc+=(i-1)/2;
		if(czc>=m){
			a[i]+=2*(czc-m);
			ll add=1e9;
			for (ll j=n;j>i;j--){
				a[j]=(add-=(a[i]+1));
			} 
			fg=1;
			break;
		}
	}
	if(!fg) {
		printf("-1\n"); 
	}else{
		for(ll i=1;i<=n;i++){
			printf("%lld ",a[i]);
		}
	}
	return 0;
}

「CF487C」 Prefix Product Sequence

由于我们的\((n-1)!\)(就是n-1位置的前缀积)包含了\(n\)的所有因数(除了\(n\)),

所以\((n-1)! \space mod \space n\)必然为0,

但是如果\(n\)为质数肯定就不会出现这种情况(因为前面没有ta的因数(1不算))

所以\(n\)如果是合数的话,肯定就GG

这里有特殊情况:4

因为\(4=2^2\)

4也是可以的,所以需要特判一下

我们的1必须放在第一个,因为如果放1在中间的话,那当前位置与前一个的前缀积就是一样的,mod n的值自然一样,就GG了

我们的n必须放在最后一个,因为如果放n在中间的话,那当前位置及以后的前缀积mod n的值都为0,就GG了

考虑剩下的n-2个数

\(\frac{2}{1},\frac{3}{2},\frac{4}{3},...,\frac{n-1}{n-2},\frac{n}{n-1}\)

这样的序列是完全没有问题的

因为第\(i\)项的前缀积为\(i\)

然后我们通过逆元可以将分数转换为整数就🆗了

int main(){
	pre();//质数
	scanf("%lld",&n);
	if(n==4||!p[n]){
		printf("YES\n");
		if(n==4){
			printf("1\n3\n2\n4\n");//特判
			return 0;
		}
	}else{
		printf("NO\n");
		return 0;
	}
	a[1]=1,a[n]=n;
	for(ll i=2;i<n;i++){
		a[i]=power(i-1,n-2)%n*i%n; 
	} 
	for(ll i=1;i<=n;i++){
		printf("%lld\n",a[i]);
	}
	return 0;
}

「GYM101611C」 Carpet

就是先走轻链,放在当前点的上一层

走重链的话,就放在这一层。。。。

没了

void dfs(ll x,ll fa){
	sz[x]=1;
	son[x]=-1;
	for(ll i=hd[x];i;i=nxt[i]){
		ll y=to[i];
		if(y==fa) continue;
		f[y]=x;
		dfs(y,x);
		sz[x]+=sz[y];
		if(son[x]==-1||sz[son[x]]<sz[y]) son[x]=y;
	}
}
void dfs1(ll x,ll yy){
	//cout<<"---"<<x<<" "<<yy<<"\n";
	X[x]=++c[yy],Y[x]=yy;
	if(son[x]==-1){
		return ;
	}
	for(ll i=hd[x];i;i=nxt[i]){
		ll y=to[i];
		if(y==f[x]||y==son[x]) continue;		
		dfs1(y,yy+1);
	} 
	dfs1(son[x],yy);
}
posted @ 2022-08-26 15:41  _Youngxy  阅读(104)  评论(0)    收藏  举报