Codeforces Round #665 (Div. 2) 题解

A

稍微分类讨论一下即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 1000010

int T,n,k;

int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d %d",&n,&k);
		if(n<k)printf("%d\n",k-n);//右移k-n步,然后B可以选择在A点右边任意一个位置
		else{
    
    
			if((n-k)%2)printf("1\n");//将B放在OA上,只有(n-k)%2=0时才能找到这样的B
			else printf("0\n");
		}
	}
}

B

发现其实会产生贡献的只有 ( 2 , 1 ) (2,1) (2,1) ( 1 , 2 ) (1,2) (1,2),使前者尽可能多,后者尽可能少即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int T,a,b,c,x,y,z;

int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d %d %d %d %d %d",&a,&b,&c,&x,&y,&z);
		int ans=0;
		int p=min(c,y);c-=p,y-=p;ans+=2*p;//2 1
		p=min(a,z);a-=p;z-=p;//0 2
		p=min(b,x);b-=p;x-=p;//1 0
		p=min(b,y);b-=p;y-=p;//1 1
		p=min(c,z);p-=c;p-=z;//2 2
		p=min(b,z);ans-=2*p;//1 2
		printf("%d\n",ans);	
	}
}

C

可以发现,对于所有最小值的倍数,两两之间都可以通过与最小值交换来进行相互交换,而对于不是最小值倍数的数,一定不可能与别的数交换,所以找到所有最小值的倍数然后给他们排个序,再看看序列是否有序即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 100010

int T,n,a[maxn];
vector<int>b;
bool v[maxn];

int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d",&n);
		int mi=1e9;
		for(int i=1;i<=n;i++){
    
    
			scanf("%d",&a[i]);
			mi=min(mi,a[i]);
		}
		if(mi==1){
    
    printf("YES\n");continue;}
		bool ans=true;b.clear();
		for(int i=1;i<=n;i++){
    
    
			if(a[i]%mi==0){
    
    
				int c=a[i],d=0;
				while(c%mi==0)c/=mi,d++;
				b.push_back(a[i]);v[i]=true;
			}
		}
		sort(b.begin(),b.end());int st=0;
		for(int i=1;i<=n;i++)if(v[i])a[i]=b[st++],v[i]=false;
		for(int i=1;i<n;i++)if(a[i]>a[i+1])ans=false;
		if(ans)printf("YES\n");
		else printf("NO\n");
	}
}

D

主要思路是将因子尽可能放在贡献最大的边上,还要注意他要求边权为 1 1 1 的边最少,所以要尽量先给每条边一个因子(大的因子放贡献大的边上),剩余的因子全部堆到贡献最大的边上即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 200010
#define mod 1000000007

int T,n,m,c[maxn],ans;
struct edge{
    
    int y,next;}e[maxn<<1];
int first[maxn],len=0;
void buildroad(int x,int y){
    
    e[++len]=(edge){
    
    y,first[x]};first[x]=len;}
int size[maxn];
struct par{
    
    
	int x,y;
	par(int xx=0,int yy=0):x(xx),y(yy){
    
    }
};
vector<par>q;
void dfs(int x,int fa){
    
    
	size[x]=1;
	for(int i=first[x],y;i;i=e[i].next)
	if((y=e[i].y)!=fa){
    
    
		dfs(y,x),size[x]+=size[y];
		q.push_back(par(min(size[y],n-size[y]),1ll*size[y]*(n-size[y])%mod));
	}
}
bool cmp1(par x,par y){
    
    return x.x>y.x;}
bool cmp2(int x,int y){
    
    return x>y;}

int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d",&n);ans=0;
		for(int i=1,x,y;i<n;i++)scanf("%d %d",&x,&y),
		buildroad(x,y),buildroad(y,x);
		q.clear();dfs(1,0);sort(q.begin(),q.end(),cmp1);
		scanf("%d",&m);
		for(int i=1;i<=m;i++)scanf("%d",&c[i]);
		while(m<n-1)c[++m]=1;
		sort(c+1,c+m+1,cmp2);
		ans=q[0].y;
		for(int i=1;i<=m-(n-2);i++)ans=1ll*ans*c[i]%mod;
		for(int i=1;i<n-1;i++)ans=(ans+1ll*q[i].y*c[m-(n-2)+i]%mod)%mod;
		printf("%d\n",ans);
		for(int i=1;i<=n;i++)first[i]=0;len=0;
	}
}

E

容易发现,对于一条横线,每与一条竖线相交,那么总的面数就会 + 1 +1 +1

所以事实上数一下交点数就做完了,实现的话随便找个数据结构就好。

代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define N 1000000
#define ll long long
 
int n,m;
ll ans=1;
struct par{
    
    
	int h,l;
	par(int x=0,int y=0):h(x),l(y){
    
    }
};
vector<par>l,r;
int u[N+10],d[N+10];
struct tree{
    
    
	int tr[N+10];
	void init(){
    
    memset(tr,0,sizeof(tr));}
	void add(int x){
    
    for(;x<=N;x+=(x&-x))tr[x]++;}
	int sum(int x){
    
    int re=0;for(;x;x-=(x&-x))re+=tr[x];return re;}
}U,D;
bool cmp1(par x,par y){
    
    return x.l<y.l;};
bool cmp2(par x,par y){
    
    return x.l>y.l;};
 
int main()
{
    
    
	scanf("%d %d",&n,&m);
	l.clear();r.clear();
	for(int i=1;i<=n;i++){
    
    
		int y,lx,rx;
		scanf("%d %d %d",&y,&lx,&rx);
		if(lx==0)l.push_back(par(y,rx));
		else r.push_back(par(y,lx));
	}
	sort(l.begin(),l.end(),cmp1);
	sort(r.begin(),r.end(),cmp2);
	for(int i=1;i<=m;i++){
    
    
		int x,ly,ry;
		scanf("%d %d %d",&x,&ly,&ry);
		if(ly==0){
    
    
			d[x]=ry;
			if(ry==N)ans++;
		}
		else u[x]=N-ly;
	}
	
	U.init();D.init();int now=0;
	for(int i=0;i<=N;i++){
    
    
		if(d[i])D.add(d[i]);
		if(u[i])U.add(u[i]);
		if(i==N)D.add(N);
		while(now<l.size()&&l[now].l==i){
    
    
			ans+=D.sum(N)-D.sum(l[now].h-1);
			ans+=U.sum(N)-U.sum(N-l[now].h-1);
			now++;
		}
	}
	
	U.init();D.init();now=0;
	for(int i=N;i>=0;i--){
    
    
		if(d[i])D.add(d[i]);
		if(u[i])U.add(u[i]);
		if(i==0)D.add(N);
		while(now<r.size()&&r[now].l==i){
    
    
			ans+=D.sum(N)-D.sum(r[now].h-1);
			ans+=U.sum(N)-U.sum(N-r[now].h-1);
			now++;
		}
	}
	
	printf("%lld",ans);
}

F

先造一棵线段树来维护这个东西,对于每一个节点,维护一个 lazy \text{lazy} lazy 数组,表示子树内每个深度的节点是否需要交换左右子树。

对于 S w a p Swap Swap 操作,给根节点在对应深度打一个 lazy \text{lazy} lazy 标记就行了。

对于 R e v e r s e Reverse Reverse 操作,类比文艺平衡树那题,要使得每个长度为 2 k 2^k 2k 的区间都翻转,只需要让深度为 n − k n-k nk 以下的节点全部交换左右儿子即可。

修改操作和查询操作直接进行即可,这样 O ( n q ) O(nq) O(nq) 已经可以AC了。

但是实现时其实并不用那么麻烦,注意到上面这个做法有一个特点,每个节点在操作时是不会被拆开的,即一个节点永远管理同一堆数。

而每次操作时都是对全局进行操作,我们可以将 S w a p Swap Swap R e v e r s e Reverse Reverse 操作看做对下标的修改, S w a p Swap Swap 是异或 2 k 2^k 2k R e v e r s e Reverse Reverse 是异或 2 k − 1 2^k-1 2k1,和上面的交换左右儿子是对应的,用一个 n o w now now 记录全局下标变化值即可。询问时,我们将询问的区间拆开成线段树上对应的节点,找到这些节点在异或 n o w now now 之后对应的节点,然后求和即可。

代码如下:

#include <cstdio>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn (1<<19)
#define ll long long
#define zuo ch[0]
#define you ch[1]

int n,m,a[maxn],now=0;
struct node *root=NULL;
struct node{
    
    
	int l,r,mid;ll z;node *ch[2];
	node(int x,int y):l(x),r(y),mid(l+r>>1){
    
    
		if(x<y){
    
    
			zuo=new node(x,mid);
			you=new node(mid+1,y);
			z=zuo->z+you->z;
		}else z=a[x],zuo=you=NULL;
	}
	void change(int x){
    
    
		if(l==r)return (void)(z=a[x]);
		ch[x>=mid+1]->change(x);
		z=zuo->z+you->z;
	}
	ll getsum(int x,int len){
    
    
		if(r-l+1==len)return z;
		return ch[x*len>=mid+1]->getsum(x,len);
	}
	ll split(int x,int y){
    
    
		if(l==x&&r==y)return root->getsum( (x/(r-l+1)) ^ (now>>(int)(log2(r-l+1))) ,r-l+1);
		if(x<=mid&&y>=mid+1)return zuo->split(x,mid)+you->split(mid+1,y);
		else return ch[x>=mid+1]->split(x,y);
	}
};

int main()
{
    
    
	scanf("%d %d",&n,&m);n=1<<n;
	for(int i=0;i<n;i++)scanf("%d",&a[i]);
	root=new node(0,n-1);
	for(int i=1,id,x,y;i<=m;i++){
    
    
		scanf("%d",&id);
		if(id==1){
    
    
			scanf("%d %d",&x,&y);x--;
			a[x^now]=y;root->change(x^now);
		}else if(id<4){
    
    
			scanf("%d",&x);
			now^=(1<<x)-(id==2);
		}else{
    
    
			scanf("%d %d",&x,&y);x--;y--;
			printf("%lld\n",root->split(x,y));
		}
	}
}

猜你喜欢

转载自blog.csdn.net/a_forever_dream/article/details/108260440