【Codeforces】Round #510 (Div. 2) C,D,E,F

版权声明:转载请附带链接或评论 https://blog.csdn.net/corsica6/article/details/82757745

传送门:codeforcesRound510Div2



C. Array Product

题解

模拟题。
读入的时候就先把所有的零和正数分别合并,若有奇数个负数,考虑把奇数个负数和零合并后一并删除。
删除需要判断是否还有剩下的数。

一堆判断,难打qwq

代码

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;

int nr,n,pre,a[N],num;
int dn[N],cnt,zr,mx=-2e9,id;

int main(){
	int i,j;
	scanf("%d",&n);num=n;
	for(i=1;i<=n;++i){
		scanf("%d",&a[i]);
		if(a[i]<0) {dn[++cnt]=i;if(mx<a[i]) {mx=a[i];id=i;}}
		else if(a[i]==0) {if(zr) printf("1 %d %d\n",zr,i),num--;zr=i;}
		else{if(nr) printf("1 %d %d\n",nr,i),num--;nr=i;}
	}
	if(cnt&1){
		if(zr) {printf("1 %d %d\n",zr,id);num--;}
		if(num>1) {printf("2 %d\n",id);num--;}
		for(i=1;i<=cnt;++i) if(dn[i]!=id) break;
	    for(j=i+1;j<=cnt;++j) if(dn[j]!=id)
		 printf("1 %d %d\n",dn[j],dn[i]);
	    if(nr && i<=cnt) printf("1 %d %d\n",nr,dn[i]);
	}else{
		for(i=2;i<=cnt;++i) {printf("1 %d %d\n",dn[i],dn[1]);num--;}
		if(zr && num>1) printf("2 %d\n",zr),num--;
		if(nr && cnt) printf("1 %d %d\n",nr,dn[1]); 
	}
	return 0;
}

D. Petya and Array

题解

信仰一交。没有离散化,直接动态开点线段树就过了。

代码

#include<bits/stdc++.h>
#define mid (((l)+(r))>>1)
using namespace std;
typedef long long ll;
const int N=2e7+10;
const ll MX=2e14;
int n,m,k,ls[N],rs[N],sz[N],cnt;
ll t,a,ans;int re,rt;

inline void ins(int &k,ll l,ll r,ll pos)
{
	if(!k) k=++cnt;
	sz[k]++;
	if(l==r) return;
	if(pos<=mid) ins(ls[k],l,mid,pos);
	else ins(rs[k],mid+1,r,pos);
}

inline int ask(int k,ll l,ll r,ll L,ll R)
{
	if(!k) return 0;
	if(L<=l && r<=R)return sz[k];
	int re=0;
	if(L<=mid) re+=ask(ls[k],l,mid,L,R);
	if(R>mid) re+=ask(rs[k],mid+1,r,L,R);
	return re;
}

int main(){
	int i;ll bs;
	scanf("%d%I64d",&n,&t);
	ins(rt,-MX,MX,0);
	for(i=1;i<=n;++i){
	   scanf("%d",&re);
	   a+=re;
	   ans+=ask(rt,-MX,MX,a-t+1,MX);
	   ins(rt,-MX,MX,a);
	} 
	printf("%I64d\n",ans);
}

E. Vasya and Magic Matrix

题解

死于读题。分数只是个限制,与得分无关,得分只是距离的平方。

所有对于当前点 ( R , C ) (R,C) ,假设有 k k 个值小于它的位置 ( r i , c i ) ( 1 i k ) (r_i,c_i)(1\leq i\leq k) 。若当前已经处理好这 k k 个位置的期望 E i ( 1 i k ) E_i(1\leq i\leq k) ,则转移为:
E ( R , C ) = 1 k i = 1 k ( e i + ( R r i ) 2 + ( C c i ) 2 ) E_{(R,C)}=\frac 1k\sum_{i=1}^k (e_i+(R-r_i)^2+(C-c_i)^2)
这样的转换是 O ( n 2 ) O(n^2) 级别的复杂度,但和式往往可以通过交换和号等方法变为求前缀和使得复杂度降为 O ( n ) O(n) ,于是得到:
E ( R , C ) = R 2 + C 2 + 1 k i = 1 k ( e i + r i 2 + c i 2 ) 2 R k i = 1 k r i 2 C k i = 1 k c i E_{(R,C)}=R^2+C^2+\frac 1k\sum_{i=1}^k(e_i+r_i^2+c_i^2)-\frac {2R}{k}\sum_{i=1}^kr_i-\frac{2C}{k}\sum_{i=1}^kc_i
考虑到值相同的点可能有多个,用滚动数组存小于当前值的前缀和转换即可。
其实只需要一个变量存,写复杂了

代码

#include<bits/stdc++.h>
#define gc getchar()
#define si isdigit(ch) 
using namespace std;
const int mod=998244353;
const int N=1e6+10;

int st,n,m,R,C,nv[N],cnt,f[2][5];
//f[0]-> ev_i | f[1]->r_i | f[2]->c_i | f[3]->c_i^2+r_i^2

struct P{
   int x,y,v;
   P(){};
   P(int XX,int YY,int VV){x=XX;y=YY;v=VV;}
   bool operator <(const P&ky)const{return v<ky.v;}
}q[N];

char ch;
inline int rd()
{
	ch=gc;int x=0,f=1;
	for(;!si;ch=gc) if(ch=='-') f=-1;
	for(;si;ch=gc) x=x*10+(ch^48);
	return x*f;
}

inline int mul(int x,int y){return 1ll*x*y%mod;}
inline int ad(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dc(int x,int y){x-=y;return x<0?x+mod:x;}
inline int sqr(int x){return mul(x,x);}

inline int fp(int x,int y)
{
	int re=1;
	for(;y;y>>=1,x=mul(x,x))
	 if(y&1) re=mul(re,x);
	return re;
}

inline int cal(int pre,int id,int sta)
{
	if(pre==0) return 0;
	int re=ad(sqr(q[id].x),sqr(q[id].y));
	re=ad(re,mul(nv[pre],ad(f[sta][0],f[sta][3])));
	re=dc(re,mul(mul(q[id].x,mul(2,nv[pre])),f[sta][1]));
	re=dc(re,mul(mul(q[id].y,mul(2,nv[pre])),f[sta][2]));
	return re;
}

int main(){
	int i,j,k,t,lim,pr=0;
	scanf("%d %d",&n,&m);
	lim=n*m;
	nv[0]=nv[1]=1;
	for(i=2;i<=lim;++i) nv[i]=mul(mod-mod/i,nv[mod%i]);
	for(i=1;i<=n;++i)
	 for(j=1;j<=m;++j)
	 	q[++cnt]=P(i,j,rd());
	sort(q+1,q+cnt+1);
	scanf("%d%d",&R,&C);
	for(st=1;st<=cnt;++st)
	 if(q[st].x==R && q[st].y==C) break;
	lim=q[st].v;
	for(i=j=1;q[i].v<lim;i=j){
		pr^=1;
		memcpy(f[pr],f[pr^1],sizeof(f[pr])); 
		for(;q[j].v==q[i].v;++j){
			f[pr][0]=ad(f[pr][0],cal(i-1,j,pr^1));
			f[pr][1]=ad(f[pr][1],q[j].x);
			f[pr][2]=ad(f[pr][2],q[j].y);
			f[pr][3]=ad(f[pr][3],ad(sqr(q[j].x),sqr(q[j].y)));
		}
	}
	printf("%d\n",cal(i-1,st,pr));
	return 0;
}

F. Leaf Sets

题解

首先随便选一个非叶节点做根。

考虑从子节点建立集合向上合并。

设当前点为 x x ,有 q q 个子节点,记录当前所有 s o n x i ( 1 i q ) son_{xi}(1\leq i\leq q) 的子树内的叶子集合相对于 x x 的最深深度 d s o n x i ( 1 i q ) d_{son_{xi}}(1\leq i\leq q) ,一个合法的集合必然满足集合内最大值和次大值深度之和 k \leq k 。两个子树的 s e t set 可以合并当且仅当 d s o n x i + d s o n x j k d_{son_{xi}}+d_{son_{xj}}\leq k 。所以将 s o n x i son_{xi} d s o n x i d_{son_{xi}} 升序排序,当 d s o n x i + d s o n x i + 1 &gt; k d_{son_{xi}}+d_{son_{xi+1}}&gt;k 时,再向上合并时若有其他集合能与 d s o n x i + 1 d_{son_{xi+1}} 合并,必然也能与 d s o n x i d_{son_{xi}} 合并。所以这里直接舍弃 s o n x i + 1 son_{xi+1} 及以后的子节点,将答案加上 q i q-i 即可。所以每次向上合并时只保留一个集合。递归到 r o o t root 后,答案再加一(当前保留的集合)即可。

注意每次向上合并时,必须要保留至少一个集合上去(即使可能 d s o n x 1 &gt; k d_{son_{x1}}&gt;k ,但这仅当在这个子树内的点与其他子树内的点合并时集合才不符合条件,对于当前集合,点集的 l c a lca 不为 x x ,所以必然要更新上去)。
这里还需要理解一下(WA了一次才发现)。

复杂度 O ( n log n ) O(n\log n) (纯粹是排序的 l o g log …)

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;

int n,K,d[N],top,mx[N],bs;
int head[N],to[N<<1],nxt[N<<1],tot;

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

vector<int>hv[N];

void dfs(int x,int fa)
{
	int i,j,pre;mx[x]=0;
	for(i=head[x];i;i=nxt[i]){
		j=to[i];if(j==fa) continue;
		dfs(j,x);hv[x].push_back(mx[j]);
	}
	top=hv[x].size();
	if(!top) {mx[x]=1;return;}
	sort(hv[x].begin(),hv[x].end());
	for(pre=hv[x][0],i=1;i<top && pre+hv[x][i]<=K;++i) 
	  pre=hv[x][i];
	bs+=top-i;mx[x]=pre+1;
}

int main(){
	int i,j,x,y;
	scanf("%d%d",&n,&K);
	for(i=1;i<n;++i){
       scanf("%d%d",&x,&y);
       d[x]++;d[y]++;
       lk(x,y);lk(y,x);
	}
	for(i=1;;++i) if(d[i]>1) break;
	dfs(i,0);
	printf("%d\n",bs+1);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/82757745