您的位置:首页 > 其它

codeforces Round #344 A~E

2016-03-19 14:40 295 查看
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
最大值。

分析:

若将区间[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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息