codeforces Round #344 A~E

A. Interview

题意:给定n(n<1000)个元素的数组a[]和n个元素的数组b[],求一段区间[l,r]使得a[l]|a[l+1]...|a[r]+b[l]|b[l+1]|...|b[r]最大。
分析:暴力枚举区间就行了。
代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1e9+7;
const LL MINT = ~0u>>1;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

const int maxn = 2000;
int  a[maxn],b[maxn];

int s1[maxn<<2],s2[maxn<<2];

void pushup(int rt)
{
	s1[rt]=s1[rt<<1]|s1[rt<<1|1];
	
	s2[rt]=s2[rt<<1]|s2[rt<<1|1];
}

void build(int l,int r,int rt)
{
	if(l==r)
	{
		s1[rt]=a[l];
		s2[rt]=b[l];
		return ;
	}
	int m=(l+r)>>1;
	build(lson);
	build(rson);
	pushup(rt);
}
pair <int ,int > query(int L,int R,int l,int r,int rt)
{
	if(L<=l && r<=R)
	{
		return make_pair(s1[rt],s2[rt]);
	}
	int m=(l+r)>>1;
	pair <int ,int > ret(0,0);
	pair <int ,int > q1(0,0),q2(0,0);
	if(L<=m)
		q1=query(L,R,lson);
	if(R>m)
		q2=query(L,R,rson);
	return make_pair(q1.first|q2.first,q1.second|q2.second);
}

int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
		scanf("%d",&b[i]);
	build(1,n,1);
	int ans = -1;
	for(int i=1;i<=n;i++)
	{
		for(int j=i;j<=n;j++)
		{
			pair <int ,int > p=query(i,j,1,n,1);
			ans =max(ans,p.first+p.second);
		}
	}
	cout<<ans;
	return 0;
}

B.print check

题意:有n*m的矩阵(n和m小于1000),有k次操作(k<100000),操作是将某一行或者某一列的值变为x。

分析:分别用一个数组记录行和列的只,以及出现的时间即可。填的时候比较行和列的时间,,,

代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1e9+7;
const LL MINT = ~0u>>1;

const int maxn = 5555;

struct node
{
	int color,w;
}row[maxn],colu[maxn];

int Map[maxn][maxn];
int main()
{
	int n,m,q;
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=q;i++)
	{
		int tp,x,c;
		scanf("%d%d%d",&tp,&x,&c);
		if(tp==1)
		{
			row[x].color=c;
			row[x].w=i;
		}
		else
		{
			colu[x].color=c;
			colu[x].w=i;
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(row[i].w>colu[j].w)
			{
				printf("%d",row[i].color);
			}
			else
			{
				printf("%d",colu[j].color);
			}
			if(j!=m)
				printf(" ");
		}
		printf("\n");
	}
	return 0;
}

C.Report

题意:给定长度为n的数组(n<20w),有m次操作(m<20w),操作是将1~x内的元素升序或者降序排序。

分析:先找到影响范围最大的那一次操作,那么之前的那些操作都可视为无效操作,,,然后把可以确定的位置上的元素填好,然后再到后面的操作里面再找影响范围最大的那一次操作.....依次内推即可

代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1e9+7;
const LL MINT = ~0u>>1;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 222222;

int a[maxn];
int n,m;

multiset <int > st;
typedef multiset <int >::iterator type;

struct node
{
	bool dir;  //true为升序 
	int len;
}manager[maxn];

int tree[maxn<<2];

void  build(int l,int r,int rt)
{
	if(l==r)
	{
		tree[rt]=manager[l].len;
		return ;
	}
	int m=(l+r)>>1;
	build(lson);
	build(rson);
	tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}
int query(int L,int R,int l,int r,int rt)
{
	if(L<=l && r<=R)
		return tree[rt];
	int m=(l+r)>>1,ret=-1;
	if(L<=m)
		ret=max(ret,query(L,R,lson));
	if(R>m)
		ret=max(ret,query(L,R,rson));
	return ret;
}

int Find(int L,int R,int v)
{
	int ret;
	for(int i=L;i<=R && query(i,R,1,m,1)==v;i++)
		if(manager[i].len==v)
			ret=i;
	return ret;
}
int ans[maxn];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		ans[i]=a[i];
		st.insert(a[i]);
	}
	for(int i=1;i<=m;i++)
	{
		int tp,x;
		scanf("%d%d",&tp,&x);
		manager[i].dir = (tp==1);
		manager[i].len=x; 
	}
	build(1,m,1);
	int perlen,perpos,curlen,curpos;
	perlen = query(1,m,1,m,1);
	perpos = Find(1,m,perlen);
	for(int i=n;i>perlen;i--)
		st.erase(st.lower_bound(a[i]));
	while(1)
	{
		if(perpos==m)
			break;
		curlen = query(perpos+1,m,1,m,1);
		curpos = Find(perpos+1,m,curlen);
		if(manager[perpos].dir) //升序 
		{
			for(int i=perlen;i>=curlen+1;i--)
			{
				type ite=--st.end();
				ans[i]=*ite;
				st.erase(ite);
			}
		}
		else
		{
			for(int i=perlen;i>=curlen+1;i--)
			{
				type ite=st.begin();
				ans[i]=*ite;
				st.erase(ite);
			}
		}
		perlen = curlen;
		perpos = curpos;
	}
//	printf("sz:%d perlen:%d\n",st.size(),perlen);
	for(int i=perlen;i>=1;i--)
	{
		if(manager[perpos].dir)
		{
			type ite = --st.end();
			ans[i]=*ite;
			st.erase(ite);
		}
		else
		{
			type ite = st.begin();
			ans[i]=*ite;
			st.erase(ite);
		}
	}
	for(int i=1;i<=n;i++)
		printf("%d ",ans[i]);
	return 0;
}

D.Messenger

题意:有两个被压缩的字符串s1和s2(可能没有被压缩完全),求s2在s1里面出现了多少次。

分析:先将串完全压缩,然后将串做特殊处理,比如6-a,100-c,21-h,那么处理后就是a6c100h21,然后利用字符串hash进行匹配就行了,有些地方要特殊处理,比如头和尾可以比s1短,但中间那一截必须和s1相同。还有就是s1只有一种字符和只有两种字符的情况。

代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 1e9+7;
const LL MINT = ~0u>>1;

const int maxn = 2e6+7;
char str1[maxn],str2[maxn];
int cnt1,cnt2;
int n,m;

struct node
{
	LL x;
	char ch;
}s1[maxn],s2[maxn];

void add(char *str,int &cnt,int d)
{
	int pos = cnt , cur = cnt;
	while(d)
	{
		str[cur++]=static_cast<char>('0'+d%10);
		d/=10;
	}
	cnt = cur--;
	while(pos < cur)
		swap(str[pos++],str[cur--]);
}

const ULL seed = 131;

ULL H[maxn],X[maxn];
void Init()
{
	H[0]=0;
	for(int i=1;i<cnt1;i++)
		H[i]=H[i-1]*seed+str1[i]-'0';
	X[0]=1;
	for(int i=1;i<cnt1;i++)
		X[i]=X[i-1]*seed;
}
ULL getHash(int i,int len)
{
	return H[i+len-1]-H[i-1]*X[len];
}
ULL cal(int m)
{
	ULL ret(0);
	cnt2=1;
	for(int i=2;i<m;i++)
	{
		str2[cnt2++]=s2[i].ch;
		add(str2,cnt2,s2[i].x);
	}
	str2[cnt2]='\0';
	for(int i=1;i<cnt2;i++)
		ret=ret*131+str2[i]-'0';
	return ret;
}

ULL solve()
{
	if(m==1)
	{
	//	printf("n:%d m:%d \n",n,m);
		ULL ans(0);
		for(int i=1;i<=n;i++) if(s1[i].ch==s2[1].ch && s1[i].x>=s2[1].x)
		{
			ans += (s1[i].x-s2[1].x+1);
		}
		cout<<ans;
		return 0;
	}
	if(m==2)
	{
		ULL ans(0);
		for(int i=1;i<n;i++) if(s1[i].ch==s2[1].ch && s1[i+1].ch==s2[2].ch)
		{
			if(s1[i].x<s2[1].x || s1[i+1].x<s2[2].x)
				continue ;
			ans++;
		}
		cout<<ans;
		return 0;
	}
	ULL str2Hash = cal(m);
	int len2 = cnt2-1;
	ULL ans = 0 ;
	for(int i=1;i<cnt1;i++) if(str1[i]==s2[1].ch)
	{
		int cur = i+1;
		LL num = 0;
		while(cur<cnt1 && '0'<=str1[cur] && str1[cur]<='9')
		{
			num = num*10 + str1[cur]-'0';
			++cur;
		}
		if(num<s2[1].x)
			continue ;
		ULL temp = getHash(cur,len2);

		if(temp != str2Hash)
			continue ;
		
		cur+=len2;
		if(str1[cur]!=s2[m].ch)
			continue ;
			
		num = 0;
		cur++;
		while(cur<cnt1 && '0'<=str1[cur] && str1[cur]<='9')
		{
			num = num*10 + str1[cur]-'0';
			++cur;
		}
		if(num<s2[m].x)
			continue ;
		
		++ans;
	}
	cout<<ans<<endl;
}

int main()
{
	str1[0]=str2[0]='*';
	cnt1=cnt2=1;
	int tempn=1,tempm=1;
	
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		int x;
		char ch[2];
		scanf("%d-%s",&x,ch);
		
		if(s1[tempn-1].ch==ch[0])
		{
			s1[tempn-1].x+=x;
			continue ;
		}
		else
		{
			s1[tempn].x=x;
			s1[tempn].ch=ch[0];
			tempn++;
			continue ;
		}
	}
	for(int i=1;i<=m;i++)
	{
		int x;
		char ch[2];
		scanf("%d-%s",&x,ch);
		
		if(s2[tempm-1].ch==ch[0])
		{
			s2[tempm-1].x+=x;
			continue ;
		}
		else
		{
			s2[tempm].x=x;
			s2[tempm].ch=ch[0];
			tempm++;
			continue ;
		}
	}
	n=tempn-1;
	m=tempm-1;
	
	for(int i=1;i<=n;i++)
	{
		str1[cnt1++]=s1[i].ch;
		add(str1,cnt1,s1[i].x);
	}
	str2[cnt2]=str1[cnt1]='\0';
	
	Init();
	solve();
//	cout<<str1<<"\n"<<str2<<endl;
	return 0;
}

E.product sum (斜率优化--可做模版)

题意:给定长度为n(n<=20 000)的数组a[],你可以选择一个区间[l,r],将[l,r]里面的数循环左移一下或者右移一下,求‘1×a[1]+2×a[2]+...n×a[n]最大值。

分析:

若将区间[l,r]循环右移。

元区间l*a[l]+(l+1)*a[l+1]+(l+2)*a[l+2]+...+r*a[r] 

-----> l*a[r]+(l+1)*a[l]+(l+2)*a[l+1]+...+r*a[r-1]

增量delta = l*a[r]+a[l]+a[l+1]+...+a[r-1]-r*a[r]

               =(l-r)*a[r]+a[l]+a[l+1]...+a[r-1]

               =(l-r)*a[r]+Sum[r-1]-Sum[l-1]

               =(Sum[r-1]-r*a[r])+(l*a[r]-Sum[l-1])

当我们枚举r的时候,r可以看成定值。(Sum[r-1]-r*a[r])为定值,a[r]已知,而且一个l对应一个Sum[l-1],而l此时的范围为[1,r-1],现在的问题是在r-1条直线里面选出一条,将a[r]代入,使得l*a[r]-Sum[l-1]最大。

如果直接枚举l的必然超时,这个问题可以用斜率优化解决。

斜率优化的做法和思想就是,首先将直线按斜率排序,利用直线的交点的关系,将无用的直线去掉,然后剩下的每一条直线有一部分在其他的直线的上面,当询问x时,利用二分,找到x的最合适的区间。

代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int maxn = 2e5+7;
LL a[maxn],n,sum[maxn];

struct line
{
	LL k,b;
	line(LL tk=0,LL tb=0):
		k(tk),b(tb){}
	LL getValue(LL x)
	{
		return k*x+b;
	}
};

class ConvexHull    //维护一个凸壳 
{
	public :
		ConvexHull(int sz)
		:size(0)
		{
			hull = new line[sz+7];
		}
		void clear(){size=0;}
		void add_line(LL k,LL b)
		{
			hull[size++]=line(k,b);
			while(size>2 && is_bad(hull[size-2],hull[size-1],hull[size-3])) 
			{
				hull[size-2]=hull[size-1];
				--size;
			}
		}
		LL queryMax(LL x)
		{
			int down=-1,mid,up=size-1; //(-1,size-1]
			while(up - down > 1)
			{
				mid = (down+up)>>1;
				if(hull[mid].getValue(x)<=hull[mid+1].getValue(x))
					down = mid;
				else
					up = mid;
			}
			return hull[up].getValue(x);
		}
	private :
		int size;
		line *hull;
		bool is_bad(const line &cur,const line &next,const line &per) //判断两交点的位置 
		{/*判断per和cur的交点 cur和next的交点的位置关系*/
			return  (per.b-cur.b)*(next.k-cur.k)>=(cur.b-next.b)*(cur.k-per.k);
		}
};

int main()
{
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		sum[i]=a[i]+sum[i-1];
	}
	
	ConvexHull hull = ConvexHull(n);
	
	LL ans=0,delta=0;
	for(int i=1;i<=n;i++)
		ans += i*a[i];
	
	hull.clear();
	for(int r=2;r<=n;r++)
	{
		hull.add_line(r-1,-sum[r-2]);
		delta = max(delta,sum[r-1]-r*a[r]+hull.queryMax(a[r]));
	}
	
	hull.clear();
	for(int l=n-1;l>=1;l--)
	{
		hull.add_line(-(l+1),-sum[l+1]);
		delta = max(delta,sum[l]-l*a[l]+hull.queryMax(-a[l]));
	}
	cout<<ans+delta;
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值