Codeforces Round #538 (Div. 2)

D.

题意:给你一个颜色序列,把相邻颜色相同的一坨看做一个块,我们可以改变一个块的颜色,然后显然可以合并相邻的颜色块,问最终合并成一个块需要几步

解析:显然一个连续的区间最终变成的颜色就是最左边或者最右边的颜色,所以直接dp即可

#include<bits/stdc++.h>
using namespace std;
const int MAXN=5010;
int f[MAXN][MAXN][2];
int n,a[MAXN];
int solve(int l,int r,int k)
{
	if (f[l][r][k]!=-1) 
		  return f[l][r][k];
	if (k==0) {
		f[l][r][k]=min(solve(l+1,r,0)+(a[l+1]==a[l]?0:1),solve(l+1,r,1)+(a[r]==a[l]?0:1));
	}
	else f[l][r][k]=min(solve(l,r-1,0)+(a[l]==a[r]?0:1),solve(l,r-1,1)+(a[r]==a[r-1]?0:1));
    return f[l][r][k];
}
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++) 
		 scanf("%d",&a[i]);
	memset(f,255,sizeof(f));
	for (int i=1;i<=n;i++) f[i][i][0]=f[i][i][1]=0;	
	printf("%d\n",min(solve(1,n,0),solve(1,n,1)));	
}

E.

题目描述:这是一道交互题,你知道序列的长度,且这个数列是一个打乱的等差数列,你有两种询问,一种是询问数列中是否有大于x的数,第二种是询问序列中第x个数是什么,然后让你确定这个数列

题目解析:显然我们可以经过log1e9次操作确定这个序列的最大数,然后我们问这个等差d很难确定,由于这个序列是被打乱的,

所以我们只能随机一些位置,那么从而通过这样的方式来得到d(直接gcd即可)

显然这个随机是比较重要的,然后我的随机函数竟然比不上10~20遍的random_shuffle(晕)

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
int n,sum;
int a[100],b[1000010];
int ans,d,x,len;
void s(int l,int r)
{
	if (l==r) {
		ans=l; return;
	}
	else {
		int mid=(l+r)/2;
		int x;
		cout << '>' << ' ' << mid << endl;
		sum++; 
		fflush(stdout);
		cin >> x;
		if (x==0) s(l,mid); else s(mid+1,r);
	}
}
int gcd(int x,int y)
{
	if (!x||!y) return x+y;
	int z=x%y;
	while (z) {
		x=y; y=z; z=x%y;
	}
	return y;
}
int main(){
	scanf("%d",&n);
	s(1,inf);
	for (int i=1;i<=n;i++) b[i]=i;
	for (int i=1;i<=20;i++) random_shuffle(b+1,b+n+1);
	srand(time(NULL));
	for (int i=1;i<=min(60-sum,n);i++) 
	{
        cout << '?' << ' ' << b[i] << endl;
        fflush(stdout);
        cin >> a[++len];
	}
	sort(a+1,a+len+1);
	for (int i=1;i<len;i++) d=gcd(d,a[i+1]-a[i]);
	int x1=ans-(n-1)*d;
	cout << '!' << ' ' << x1 << ' ' << d << endl;
}

F.

题意:你有一个长度为n的序列a,而且你有q次操作,操作分为两种:第一中是给区间[l,r]中的数乘上x,第二种是询问区间[l,r]中的所有的数的积的欧拉函数值,modulo 1e9+7

解析:题目中有个性质就是不管是x,还是原来的ai他们都不会超过300,然后打表一看就发现300以内的质数的个数为62,所以我们可以用二进制来维护每一个质数是否出现,所以就是简单的线段树区间乘,区间或(为什么可以这么做?因为欧拉函数的计算公式)

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL mod=1e9+7;
const int MAXN=4e5+10;
struct node{
	LL po1,po2,s,la;
}tree[MAXN<<2];
int n,q,len,l,r,x;
int prime[MAXN],a[MAXN];
LL w[MAXN],b[MAXN],la;
char s[10];
LL ksm(LL x,LL y)
{
	LL ans=1;
	for (;y;y>>=1,x=(x*x)%mod) if (y&1) ans=(ans*x)%mod;
	return ans;
}
LL mul(LL x,LL y)
{
	return (x*y)%mod;
}
void init()
{
	for (int i=2;i<=300;i++) 
	{
	   bool p=1;
       for (int j=2;j*j<=i;j++)
       	 if (i%j==0) {
       	 	p=0; break;
       	 }
       if (p) prime[++len]=i;	 
	}
	for (int i=1;i<=len;i++) w[i]=mul((prime[i]-1),(ksm(prime[i],mod-2)));
}
void update(int k)
{
	tree[k].s=mul(tree[k<<1].s,tree[k<<1|1].s); 
	tree[k].la=(tree[k<<1].la|tree[k<<1|1].la);
}
void build(int l,int r,int k)
{
	tree[k].po1=1;
	if (l==r) {
		tree[k].s=a[l]; tree[k].la=b[l]; 
	}
	else {
		int mid=(l+r)/2;
		build(l,mid,k<<1); build(mid+1,r,k<<1|1);
		update(k);
	}
}
LL pan(int x)
{
	LL ans=0;
	for (int i=1;i<=len;i++) 
		if (x%prime[i]==0)
			 ans|=(1ll<<(i-1));
	return ans;		
}
void pushdown(int k,int l1,int l2)
{
	if (tree[k].po1!=1)
	{
		tree[k<<1].po1=mul(tree[k].po1,tree[k<<1].po1); tree[k<<1].s=mul(tree[k<<1].s,ksm(tree[k].po1,l1));
		tree[k<<1|1].po1=mul(tree[k<<1|1].po1,tree[k].po1); tree[k<<1|1].s=mul(tree[k<<1|1].s,ksm(tree[k].po1,l2));
		tree[k].po1=1;
	}
	if (tree[k].po2)
	{
		tree[k<<1].po2|=tree[k].po2; tree[k<<1].la|=tree[k].po2; 
		tree[k<<1|1].po2|=tree[k].po2; tree[k<<1|1].la|=tree[k].po2;
		tree[k].po2=0;
	}
}
LL find_lalala(int l,int r,int k,int ll,int rr)
{
	if (l==ll&&r==rr)
	{
		la|=tree[k].la;
		return tree[k].s;
	}
	else {
		int mid=(l+r)/2;
		pushdown(k,mid-l+1,r-mid);
		if (rr<=mid) return find_lalala(l,mid,k<<1,ll,rr);
		else if (ll>mid) return find_lalala(mid+1,r,k<<1|1,ll,rr);
		else return mul(find_lalala(l,mid,k<<1,ll,mid),find_lalala(mid+1,r,k<<1|1,mid+1,rr));
		update(k);
	}
}
LL findans(int l,int r)
{
	  la=0;
      LL ans=find_lalala(1,n,1,l,r);
      for (int i=1;i<=len;i++)
      {
      	if (la&1) {
      		ans=mul(ans,w[i]);
      	}
      	la>>=1;
      }
      return ans;
}
void change(int l,int r,int k,int ll,int rr,LL x,LL y)
{
     if (l==ll&&r==rr)
     {
     	LL s1=ksm(x,r-l+1);
     	tree[k].s=(tree[k].s*s1)%mod;
     	tree[k].po1=(tree[k].po1*x)%mod;
        tree[k].la|=y;
        tree[k].po2|=y;
     }
     else {
     	int mid=(l+r)/2;
     	pushdown(k,mid-l+1,r-mid);
     	if (rr<=mid) change(l,mid,k<<1,ll,rr,x,y);
     	else if (ll>mid) change(mid+1,r,k<<1|1,ll,rr,x,y);
     	else {
     		change(l,mid,k<<1,ll,mid,x,y); change(mid+1,r,k<<1|1,mid+1,rr,x,y);
     	}
     	update(k);
     }
}
int main()
{
	scanf("%d%d",&n,&q);
	init();
	for (int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
	}
	for (int i=1;i<=n;i++) b[i]=pan(a[i]);
	build(1,n,1);
	for (int i=1;i<=q;i++)
	{
        scanf("%s",s);
        if (s[0]=='T') {
        	scanf("%d%d",&l,&r); printf("%lld\n",findans(l,r));
        }
        else {
        	scanf("%d%d%d",&l,&r,&x); change(1,n,1,l,r,x,pan(x));
        }
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值