文章目录

传送门

前言

没错因为蒟蒻太菜了这场的最后一道题也咕掉了,只有AAA至EEE的题解233

A题

传送门

题意简述:给出一棵带点权的树,根节点深度为111,现在你只知道深度为奇数的点到根路径上所有点的点权和(要求保证点权非负),问所有点的点权和最小值是多少。


思路:

显然让深度为偶数的点点权尽量大是最优策略。

代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
	int ans=0,w=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans*w;
}
typedef long long ll;
const int N=1e5+5,inf=0x3f3f3f3f;
int n,s[N],a[N],dep[N],fa[N],mn[N];
vector<int>e[N];
void dfs(int p){
	if(dep[p]&1)mn[p]=inf;
	for(ri i=0,v;i<e[p].size();++i){
		dep[v=e[p][i]]=dep[p]^1,dfs(v);
		if(dep[p]&1){
			mn[p]=min(mn[p],s[v]);
			if(s[v]<s[fa[p]])puts("-1"),exit(0);
		}
	}
	if(dep[p]&1){
		if(e[p].size()){
			a[p]=mn[p]-s[fa[p]];
			for(ri i=0,v;i<e[p].size();++i)a[v=e[p][i]]=s[v]-s[fa[p]]-a[p];
		}
		else a[p]=0;
	}
}
int main(){
	n=read();
	for(ri i=2;i<=n;++i)e[(fa[i]=read())].push_back(i);
	for(ri i=1;i<=n;++i)s[i]=read();
	a[1]=s[1],dfs(1);
	ll ans=0;
	for(ri i=1;i<=n;++i)ans+=a[i];
	cout<<ans;
	return 0;
}

B题

传送门

题意简述:给你一个表格,上面只有四种字符A,T,C,GA,T,C,GA,T,C,G,你可以把任意位置上的字符替换成这四种中的一种,问所有满足在替换之后对于每个2∗22*22∗2的字正方形四种字符全部都出现的表格中跟原表格相比替换的字符数最小值是多少,并输出任意一种替换之后的表格。


思路:考虑到最终的表格要么每一行只有两种字符,要么每一列只有两种字符,于是枚举左上角2∗22*22∗2的正方形的方法再枚举是每一行两种字符还是每一列两种字符更新答案即可。

代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int N=3e5+5;
int p[4],tmp[4],ans,type,n,m,h[N][4][2],l[N][4][2];
vector<int>a[N],print[N];
char s[N];
inline int idx(const char&x){return x<='C'?(x=='A'?0:1):(x=='T'?2:3);}
inline void Print(const int&x){cout<<(x<2?(x==0?'A':'C'):(x==2?'T':'G'));}
int main(){
	scanf("%d%d",&n,&m),ans=0;
	for(ri i=0;i<n;++i){
		scanf("%s",s);
		for(ri j=0;j<m;++j)a[i].push_back(idx(s[j]));
	}
	for(ri i=0;i<n;++i)print[i].resize(m);
	for(ri i=0;i<n;++i)for(ri j=0;j<m;++j)++h[i][a[i][j]][j&1],++l[j][a[i][j]][i&1];
	for(ri i=0;i<4;++i)p[i]=3-i;
	for(ri tt=1,cnt;tt<=24;++tt){
		next_permutation(p,p+4);
		cnt=0;
		for(ri i=0;i<n;++i)cnt+=max(h[i][p[(i&1)*2]][0]+h[i][p[(i&1)*2+1]][1],h[i][p[(i&1)*2]][1]+h[i][p[(i&1)*2+1]][0]);
		if(cnt>=ans){
			type=1,ans=cnt;
			for(ri i=0;i<4;++i)tmp[i]=p[i];
		}
		cnt=0;
		for(ri i=0;i<m;++i)cnt+=max(l[i][p[(i&1)*2]][0]+l[i][p[(i&1)*2+1]][1],l[i][p[(i&1)*2]][1]+l[i][p[(i&1)*2+1]][0]);
		if(cnt>=ans){
			type=2,ans=cnt;
			for(ri i=0;i<4;++i)tmp[i]=p[i];
		}
	}
	if(type==1){
		for(ri i=0,x,y;i<n;++i){
			x=tmp[(i&1)*2],y=tmp[(i&1)*2+1];
			if(h[i][x][0]+h[i][y][1]<h[i][x][1]+h[i][y][0])swap(x,y);
			for(ri j=0;j<m;++j){
				print[i][j]=j&1?y:x;
			}
		}
	}
	else{
		for(ri i=0,x,y;i<m;++i){
			x=tmp[(i&1)*2],y=tmp[(i&1)*2+1];
			if(l[i][x][0]+l[i][y][1]<l[i][x][1]+l[i][y][0])swap(x,y);
			for(ri j=0;j<n;++j){
				print[j][i]=j&1?y:x;
			}
		}
	}
	for(ri i=0;i<n;++i,puts(""))for(ri j=0;j<m;++j)Print(print[i][j]);
	return 0;
}

C题

传送门

题意简述:问你能不能构造出一棵nnn个点的树,使得每个点的sizesizesize加起来等于sss,如果能输出使得这个树的叉数最小的任意一种方案。


思路:

先特判掉非法情况:sss过大的情况。

然后有个显然的性质:∑sizei=∑depthi\sum size_i=\sum depth_i∑sizei​=∑depthi​,知道了这个之后就可以从111开始枚举这个树是几叉树,显然在所有kkk叉树中完全kkk叉树的depthdepthdepth之和是最小的。

因此如果当前完全kkk叉树的depthdepthdepth之和大于sss是不可能的,直接考虑k+1k+1k+1叉树的情况。

否则当前一定可以构造出一种方案,我们把除最深点到根以外的所有点一个一个往当前最深点接即可,接到最后如果再接depthdepthdepth之和会超过sss的话一定可以找到一个之前接上的点使得把当前点接上去之后所有点的depthdepthdepth之和刚好等于sss。

注意细节

代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,fa[N],dept[N];
ll s;
bool vis[N];
inline void solve(const int&x){
	int tot=1;
	queue<int>q;
	q.push(1);
	while(!q.empty()){
		int p=q.front();
		q.pop(),dept[p]=dept[fa[p]]+1;
		for(ri i=1;i<=x&&tot<n;++i)q.push(++tot),fa[tot]=p;
	}
}
inline void check(const int&x){
	ll cnt=1,mul=1,dep=1;
	ll sum=1;
	while(cnt<n){
		mul*=x,cnt+=mul,++dep,sum+=(ll)dep*mul;
	}
	sum-=(ll)dep*(cnt-n);
	if(sum>s)return;
	puts("Yes");
	solve(x);
	int p=cnt-mul+1,pre=p;
	while(p)vis[p]=1,p=fa[p];
	p=pre;
	queue<int>q;
	for(ri i=n;i;--i)if(!vis[i])q.push(i);
	while(s>sum){
		p=q.front(),q.pop(),sum-=dept[p];
		fa[p]=pre,dept[p]=dept[fa[p]]+1,pre=p,sum+=dept[p];
	}
	if(sum>s){
		for(ri i=pre;i;i=fa[i]){
			if(sum-dept[pre]+dept[i]+1==s){
				fa[pre]=i,sum-=dept[pre],dept[pre]=dept[i]+1,sum+=dept[pre];
				break;
			}
		}
	}
	for(ri i=2;i<=n;++i)cout<<fa[i]<<' ';
	exit(0);
}
int main(){
	cin>>n>>s;
	if(s>(ll)(n+1)*n/2)return puts("No"),0;
	for(ri i=1;i<n;++i)check(i);
	return puts("No"),0;
}

D题

传送门

题意简述:现在有一个大鱼吃小鱼的游戏,每条鱼有一个权值,假如ai≤aja_i\le a_jai​≤aj​那么iii会被jjj吃掉并且aj+=aia_j+=a_iaj​+=ai​,如果对于两条吃与被吃的鱼满足a≤b≤2aa\le b\le 2aa≤b≤2a称之为一次危险的打斗,现在要求支持向鱼群中加入一条鱼或者删去一条鱼(鱼群最初为空),问每次操作之后对所有鱼按最优方案安排打斗顺序能够产生的危险的打斗次数的最大值。


思路:

我们称一次危险的打斗(a,b)(a≤b)(a,b) (a≤b)(a,b)(a≤b)是bbb产生的。

可以发现如果按大小把鱼排序,第一条鱼不会产生危险的打斗,一条比其之前所有鱼大小之和的两倍更大的鱼不会产生危险的打斗。

这个时候我们可以构造出一种方案使得不满足上述两点的鱼均产生一次危险的打斗。

证明可以手玩一下,就只用考虑两条最小的鱼之前是否吃过其它鱼。

然后考虑维护方式:

我们把值域分为若干区间:[1,2),[2,4),[4,8)...[1,2),[2,4),[4,8)...[1,2),[2,4),[4,8)...,不难发现,只有区间最小值才有可能成为不产生危险打斗,对每个区间分别用可删堆维护信息即可。

#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
struct In_Out_priority_queue{
	priority_queue<int,vector<int>,greater<int> >a,b;
	ll sum;
	int cnt;
	In_Out_priority_queue(){sum=cnt=0;while(!a.empty())a.pop();while(!b.empty())b.pop();}
	inline void push(const int&x){a.push(x),sum+=x,++cnt;}
	inline void del(const int&x){b.push(x),sum-=x,--cnt;}
	inline int top(){while(b.size()&&a.top()==b.top())a.pop(),b.pop();return a.top();}
}q[30];
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int main(){
	for(ri ans,tt=read(),x;tt;--tt){
		char s[2];
		scanf("%s",s),x=read();
		for(ri i=0;i<30;++i){
			if(x<(1<<(i+1))){
				if(s[0]=='+')q[i].push(x);
				else q[i].del(x);
				break;
			}
		}
		ans=0;
		ll sum=0;
		for(ri i=0;i<30;++i){
			if(!q[i].cnt)continue;
			ans+=q[i].cnt;
			if(q[i].top()>2*sum)--ans;
			sum+=q[i].sum;
		}
		cout<<ans<<'\n';
	}
	return 0;
}

E题

传送门

题意简述:先给出一个nnn个数的数组a,n≤100000a,n\le100000a,n≤100000,然后把所有aaa的子区间的gcdgcdgcd提出来排个序构成数组bbb,然后把bbb的所有子区间的和提出来排个序构成数组ccc,问ccc数组的中位数。


思路:

感觉考了好多知识点啊。

首先考虑求出数组bbb,直接用ststst表预处理+枚举子区间左端点+二分logloglog次求出不同的gcdgcdgcd段可以弄出来bbb数组。

然后考虑二分ccc数组的中位数。

考虑如何checkcheckcheck,如果bbb数组小的话貌似双指针就行了。

但现在bbb数组的长度是O(n2)O(n^2)O(n2)的直接做会爆,但是由于有很多相同的值,我们可以把相同的值压在一起,这样还是可以双指针,然后我们发现对于一个压起来的值有可能只会用到其中的一部分,考虑怎么做这个情况。

由于当前区间访问的连续性,只有一头一尾可能是访问的不完整的,因此相当于是求一个vallx+valry≤zval_lx+val_ry\le zvall​x+valr​y≤z的正整数解组数,上类欧几里得即可。

代码:

#include<bits/stdc++.h>
#define fi first
#define se second
#define ri register int
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N=50005;
int n,a[N],tot=0;
pii b[N*20];
ll lim;
vector<pii>B;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
inline int gcd(int a,int b){while(b){int t=a;a=b,b=t-t/a*a;}return a;}
namespace ST{
	int st[N][17],Log[N];
	inline void init(){
		Log[0]=-1;
		for(ri i=1;i<=n;++i)st[i][0]=a[i],Log[i]=Log[i>>1]+1;
		for(ri j=1;j<=16;++j)for(ri i=1;i+(1<<j)-1<=n;++i){
			st[i][j]=gcd(st[i][j-1],st[i+(1<<(j-1))][j-1]);
		}
	}
	inline int query(int l,int r){
		int k=Log[r-l+1];
		return gcd(st[l][k],st[r-(1<<k)+1][k]);
	}
}
inline ll calc(ll len,ll k){return k*(k+1)/2+k*(len-k);}
inline ll solve(ll a,ll b,ll c,ll t){
	if(!a)return (b/c)*(t+1);
	if(a>=c||b>=c)return solve(a%c,b%c,c,t)+t*(t+1)/2*(a/c)+(t+1)*(b/c);
	ll m=(a*t+b)/c;
	return t*m-solve(c,-b+c-1,a,m-1);
}
inline ll Calc(ll Lim,int pl,int pr){
	ll x=b[pl].fi,y=b[pr].fi,lim1=b[pl].se-1,lim2=b[pr].se-1;
	if(Lim<0)return 0;
	ll ret=0;
	if(Lim>=y*lim2){
		if(Lim>=x*lim1+y*lim2)return (lim1+1)*(lim2+1);
		ll tmp=(Lim-y*lim2)/x;
		ret+=(lim2+1)*(tmp+1),Lim-=(tmp+1)*x,lim1-=tmp+1;
	}
	if(Lim<0)return ret;
	return lim1=min(lim1,Lim/x),ret+lim1+1+solve(x,Lim-lim1*x,y,lim1);
}
inline bool check(const ll&tmp){
	ll stmp,sum=0,len=0,ans=0;
	for(ri i=1;i<=n;++i)ans+=calc((ll)b[i].se,(ll)min((ll)b[i].se,tmp/b[i].fi));
	for(ri i=1,j=1;i<=n;++i){
		while(j<i&&sum+(ll)b[i].fi*b[i].se>tmp)len-=b[j].se,sum-=(ll)b[j].fi*b[j].se,++j;
		ans+=len*b[i].se;
		stmp=sum;
		for(ri k=j-1;k&&stmp<=tmp;--k){
			ans+=Calc(tmp-stmp-b[i].fi-b[k].fi,i,k);
			stmp+=(ll)b[k].fi*b[k].se;
		}
		len+=b[i].se,sum+=(ll)b[i].fi*b[i].se;
	}
	return ans<lim;
}
int main(){
	n=read();
	for(ri i=1;i<=n;++i)a[i]=read();
	ST::init();
	for(ri i=1,x,pl,l,r,res;i<=n;++i){
		l=i;
		while(l<=n){
			res=l,r=n,x=ST::query(i,l),pl=l;
			while(l<=r){
				int mid=l+r>>1;
				if(ST::query(i,mid)==x)l=mid+1,res=mid;
				else r=mid-1;
			}
			B.push_back(pii(x,res-pl+1));
			l=res+1;
		}
	}
	sort(B.begin(),B.end());
	b[++tot]=B[0];
	for(ri i=1;i<B.size();++i)if(b[tot].fi^B[i].fi)b[++tot]=B[i];else b[tot].se+=B[i].se;
	lim=(ll)n*(n+1)/2,lim=lim*(lim+1)/2,lim=(lim+1)>>1,n=tot;
	ll l=1,r=0,res;
	for(ri i=1;i<=n;++i)r+=(ll)b[i].fi*b[i].se;
	res=r;
	while(l<=r){
		ll mid=l+r>>1;
		if(!check(mid))r=mid-1,res=mid;
		else l=mid+1;
	}
	cout<<res;
	return 0;
}

Codeforces 1098 简要题解的更多相关文章

  1. Codeforces 863 简要题解

    文章目录 A题 B题 C题 D题 E题 F题 G题 传送门 简要题解?因为最后一题太毒不想写了所以其实是部分题解... A题 传送门 题意简述:给你一个数,问你能不能通过加前导000使其成为一个回文数 ...

  2. Codeforces 381 简要题解

    做的太糟糕了...第一题看成两人都取最优策略,写了个n^2的dp,还好pre-test良心(感觉TC和CF的pretest还是很靠谱的),让我反复过不去,仔细看题原来是取两边最大的啊!!!前30分钟就 ...

  3. Codeforces 1120 简要题解

    文章目录 A题 B题 C题 D题 E题 F题 传送门 A题 传送门 题意简述:给你一个mmm个数的数列,现在规定把一个数列的1,2,...,k1,2,...,k1,2,...,k分成第一组,把k+1, ...

  4. Codeforces 1110 简要题解

    文章目录 A题 B题 C题 D题 E题 F题 G题 传送门 众所周知ldxoildxoildxoi这种菜鸡选手是不会写HHH题的,因此该篇博客只有AAA题至GGG题的题解,实在抱歉. A题 传送门 题 ...

  5. Codeforces 380 简要题解

    ABC见上一篇. 感觉这场比赛很有数学气息. D: 显然必须要贴着之前的人坐下. 首先考虑没有限制的方案数.就是2n - 1(我们把1固定,其他的都只有两种方案,放完后长度为n) 我们发现对于一个限制 ...

  6. Codeforces 845 简要题解

    文章目录 A题 B题 C题 D题 E题 F题 G题 传送门 A题 传送门 题意:2n2n2n个人下棋,分为两个阵营,每个阵营nnn个人,每个人有一个积分,积分高的能赢积分低的,问如果你可以随意选人,然 ...

  7. Codeforces 1065 简要题解

    文章目录 A题 B题 C题 D题 E题 F题 G题 传送门 GGG题略难,膜了一波zhouyuyang{\color{red} zhouyuyang}zhouyuyang巨佬的代码. 其余都挺清真的. ...

  8. Codeforces 888 简要题解

    文章目录 A题 B题 C题 D题 E题 F题 G题 传送门 A题 传送门 题意简述:给一个数列,问有多少个峰值点(同时比两边都大/小的点) 思路:按照题意模拟. 代码: #include<bit ...

  9. Codeforces 884 简要题解

    文章目录 A题 B题 C题 D题 E题 F题 传送门 A题 传送门 题意简述: 一个人要完成一件事总共需要ttt秒,现在有nnn天,每天有aia_iai​不能做事,问他可以在第几天做完. 思路:按照题 ...

随机推荐

  1. TOJ 2755 国际象棋(搜索)

    传送门:http://acm.tzc.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=2755 思路:对起点到终点进行广搜, ...

  2. Codeforces Beta Round #74 (Div. 2 Only)

    Codeforces Beta Round #74 (Div. 2 Only) http://codeforces.com/contest/90 A #include<iostream> ...

  3. Javascript 强制浏览器渲染Dom文档

    在Cordova+Framework7开发Hybrid App时,在iPhone 7上遇到一个诡异的现象(Chrome浏览器.Android都正常):js修改手风琴中的input文本框的值后,但页面仍 ...

  4. synchronized细节问题(二)

    使用synchronized声明的方法在某些情况下是有弊端的,比如A线程调用同步的方法执行一个很长时间的任务,那么B线程就必须等待比较长的时间才能执行,这样的情况下,可以使用synchronized代 ...

  5. @RequestParam使用须知

    --------------------------siwuxie095                             @RequestParam 使用须知         使用 @Requ ...

  6. 超强、超详细Redis入门教程【转】

    这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下 [本教程目录] 1.redis是什么2.redis的作者何许人也3.谁在使用red ...

  7. 干净的ssm框架项目

    其中数据库只有如下表与字段 访问效果: 项目下载: 干净的ssm框架项目.rar

  8. Java 微服务框架选型 Dubbo 和 Spring Cloud

    微服务(Microservices)是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成.系统中的各个微服务可被独立部署,各个微服务之间是松耦合的.每个微服务仅关注于完成一件任务并很好地完成该任 ...

  9. sqlserver编号

    select ROW_NUMBER() OVER (ORDER BY 字段 DESC) AS rid,* from 表名

  10. "//./root/CIMV2" because of error 0x80041003. Events cannot be delivered through this filter until the problem is corrected.

    windows系统日志错误信息: Event filter with query "SELECT * FROM __InstanceModificationEvent WITHIN 60 W ...