Codeforces Round #344 (Div. 2)

链接:http://codeforces.com/contest/631

problemA:给定两个数组A,B。设f(x,l,r)=x[l]|x[l+1]...|x[r],求max(f(A,l,r)+f(B,l,r))。

分析:因为是取或是贪心变大,所以我们把整个数组或起来即可,即l==1&&r==n。O(n)

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=1010;
const int MAX=151;
const int MOD1=1000007;
const int MOD2=100000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
const ll INF=10000000010;
typedef unsigned long long ull;
int a[N],b[N];
int main()
{
    int i,n,x=0,y=0;
    scanf("%d", &n);
    for (i=1;i<=n;i++) {
        scanf("%d", &a[i]);x|=a[i];
    }
    for (i=1;i<=n;i++) {
        scanf("%d", &b[i]);y|=b[i];
    }
    printf("%d\n", x+y);
    return 0;
}

problemB:给定n,m,k,在一个初始全为0的n*m矩阵中进行k次操作,操作一:将第ri行变为颜色ai,操作二:将第ci列变为颜色ai。输出这个矩阵最后的样子。

分析:用两个数组分别记录行和列的最后颜色即可,最后打印的时候比较下行列的先后即可。O(n*m+k)

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=5010;
const int MAX=151;
const int MOD1=1000007;
const int MOD2=100000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
const ll INF=10000000010;
typedef unsigned long long ull;
struct node {
    int time,co;
}r[N],c[N];
int main()
{
    int i,j,n,m,k,x,y,z;
    scanf("%d%d%d", &n, &m, &k);
    memset(r,0,sizeof(r));
    memset(c,0,sizeof(c));
    for (i=1;i<=k;i++) {
        scanf("%d%d%d", &x, &y, &z);
        if (x==1) { r[y].time=i;r[y].co=z; }
        else { c[y].time=i;c[y].co=z; }
    }
    for (i=1;i<=n;i++) {
        for (j=1;j<=m;j++)
        if (r[i].time>c[j].time) printf("%d ", r[i].co);
        else printf("%d ", c[j].co);
        printf("\n");
    }
    return 0;
}


problemC:给定n,m,一个长度为n的a数组,m次操作,每次操作有t,r:t==1将a[1]~a[r]排为升序,t==2将a[1]~a[r]排为降序。求m次操作之后的a数组。

分析:首先很明显如果后面有一个操作ri>现在的操作rj,那么现在的操作rj是无意义的,那么我们就可以将r操作用单调队列删除掉一些无意义的数据,那么剩下的r操作一定是降序的。那么我们先将a[1]~a[max(r)]排序,然后每次处理a[r(i-1)]~a[ri]即可,这时只要根据ti选择从排好序的数组里从左边取数或者从右边取数就好了。O(nlogn)

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=200010;
const int MAX=151;
const int MOD1=1000007;
const int MOD2=100000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
const ll INF=10000000010;
typedef unsigned long long ull;
struct node {
    int t,r;
}b[N];
int a[N],ans[N];
int main()
{
    int i,j,n,m,x,y,k=0,L,R;
    scanf("%d%d", &n, &m);
    for (i=1;i<=n;i++) {
        scanf("%d", &a[i]);ans[i]=a[i];
    }
    for (i=1;i<=m;i++) {
        scanf("%d%d", &x, &y);
        while (k>0&&(y>=b[k].r)) k--;
        if (k==0||x!=b[k].t) { k++;b[k].t=x;b[k].r=y; }
    }
    L=1;R=b[1].r;
    sort(a+1,a+b[1].r+1);
    for (i=1;i<k;i++) {
        for (j=b[i].r;j>b[i+1].r;j--)
        if (b[i].t==1) { ans[j]=a[R];R--; }
        else { ans[j]=a[L];L++; }
    }
    for (i=b[k].r;i>0;i--)
    if (b[k].t==1) { ans[i]=a[R];R--; }
    else { ans[i]=a[L];L++; }
    for (i=1;i<=n;i++) printf("%d ", ans[i]);
    printf("\n");
    return 0;
}

problemD:给定n,m,然后两行表示两个字符串t,s,{3-a 2-b 3-x}=="aaabbxxx"。求s在t中出现过多少次。

分析:首先我们先解决字符串表示不唯一的问题,即"aaa"可以为{3-a}或{2-a 1-a}或{1-a 2-a},我们将所有相邻的相同字符压缩在一起,这样对题目求解是无影响的。然后我的方法是先对s的表示长度讨论,如果lens==1||lens==2,那么我们for一遍就可以了,当lens>2时,去掉s的首尾两个表示字符将中间的去与t跑kmp,然后O(n)判一遍就行了。。PS:请忽视代码中将数字和字符合在一起离散化的傻逼操作。。正确做法应该是将kmp改成两个关键字的就行了。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=200010;
const int MAX=151;
const int MOD1=1000007;
const int MOD2=100000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
const ll INF=10000000010;
typedef unsigned long long ull;
int n,m,len1,len2;
ll x[N],y[N];
string S;
char s[N][3],t[N][3];
map<string,int>ma;
int f[N],h[N];
string getS(int x) {
    string ret="";
    while (x) {
        ret=(char)(x%10+'0')+ret;x/=10;
    }
    return ret;
}
int q[N],nex[N];
void pre_kmp(int s[],int les)
{
    int i,k;
    nex[0]=-1;k=-1;
    for (i=0;i<les;i++)  {
        while (k>-1&&s[k]!=s[i]) k=nex[k];
        k++;nex[i+1]=k;
    }
}
void kmp(int s[],int t[],int les,int let)
{
    pre_kmp(s,les);
    int i,k=0,bo=0;
    memset(q,0,sizeof(q));
    for (i=0;i<let;i++) {
        while (k>0&&s[k]!=t[i]) k=nex[k];
        if (s[k]==t[i])  k++;
        if (k==les) {
            q[i]=1;k=nex[k];
        }
    }
}
ll solve() {
    ll ret=0;
    int i,w,k=0,L,R;
    for (i=1;i<=len1;i++) {
        S=getS(x[i]);S+=s[i][1];
        w=ma[S];
        if (w) f[i-1]=w;
        else { k++;ma[S]=k;f[i-1]=k; }
    }
    for (i=2;i<len2;i++) {
        S=getS(y[i]);S+=t[i][1];
        w=ma[S];
        if (w) h[i-2]=w;
        else { k++;ma[S]=k;h[i-2]=k; }
    }
    kmp(h,f,len2-2,len1);
    for (i=len2-2;i<len1-1;i++)
    if (q[i]) {
        L=i-(len2-2)+1;R=i+2;
        if (s[L][1]==t[1][1]&&x[L]>=y[1]&&s[R][1]==t[len2][1]&&x[R]>=y[len2]) ret++;
    }
    return ret;
}
int main()
{
    ll ans=0;
    scanf("%d%d", &n, &m);
    len1=len2=0;
    for (int i=1;i<=n;i++) {
        scanf("%I64d%s", &x[i], s[i]);
        if (len1>0&&s[i][1]==s[len1][1]) { x[len1]+=x[i]; }
        else { len1++;x[len1]=x[i];s[len1][1]=s[i][1]; }
    }
    for (int i=1;i<=m;i++) {
        scanf("%I64d%s", &y[i], t[i]);
        if (len2>0&&t[i][1]==t[len2][1]) { y[len2]+=y[i]; }
        else { len2++;y[len2]=y[i];t[len2][1]=t[i][1]; }
    }
    if (len2==1) {
        for (int i=1;i<=len1;i++)
        if (s[i][1]==t[1][1]&&x[i]>=y[1]) ans+=x[i]-y[1]+1;
    } else if (len2==2) {
            for (int i=1;i<len1;i++)
            if (s[i][1]==t[1][1]&&s[i+1][1]==t[2][1]&&x[i]>=y[1]&&x[i+1]>=y[2]) ans++;
        } else ans=solve();
    printf("%I64d\n", ans);
    return 0;
}

problemE:给定一个数组a,定义一个数组的价值为sum=sigma(1~n)a[i]*i。现在允许一次操作:取出数组中一个数,然后插入到任意位置。求一次操作后的最大价值。

分析:很明显有3中情况,1:所有元素不动。2:选出的那个数向前找一个位置插入。3:选出的那个数向后插入。我们来讨论向前插入的那种情况(向后同理),对于每一个a[i]都是可能被选中的,那么对于每一个a[i]就会对应有一个a[j]有a[j]a[j+1]...a[i]=>a[i]a[j]a[j+1]...a[i-1]是使得原价值ans获得最大收益的,即我们要快速找到max(a[i]*(j-i)+sum[i-1]-sum[j-1])=>max(a[i]*j   -sum[j-1]   +sum[i-1]-a[i]*i),那么有sum[i-1]-a[i]*i是不变的,我们将j当成斜率,-sum[j-1]当成截距,然后我们只要将1~i-1形成的i-1条直线维护一个下凸包,那么对于i来说我们就可以三分快速找到那个最优的j了。最后将ans+mx就是啦。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=200010;
const int MAX=151;
const int MOD1=100000007;
const int MOD2=100000009;
const double EPS=0.00000001;
typedef long long ll;
const ll INF=10000000010;
const ll MOD=1000000007;
typedef unsigned long long ull;
struct node {
    ll l,tot;
}d[N];
int k;
ll a[N],sum[N];
ll calal(int x,int r) {
    return a[x]*d[r].l-sum[d[r].l-1]+sum[x-1]-a[x]*x;
}
ll getchangel(int x) {
    if (!k) return 0;
    int l=0,r=k,mid=k/2;
    while (l+1<r)
    if (calal(x,mid)<calal(x,mid+1)) { l=mid;mid=(l+r)/2; }
    else { r=mid;mid=(l+r)/2; }
    return calal(x,r);
}
void addl(ll l,ll tot) {
    while (k>1&&(d[k].tot-d[k-1].tot)*(d[k-1].l-l)>=(tot-d[k-1].tot)*(d[k-1].l-d[k].l)) k--;
    k++;d[k].l=l;d[k].tot=tot;
}
ll calar(int x,int r) {
    return a[x]*d[r].l-sum[d[r].l]+sum[x]-a[x]*x;
}
ll getchanger(int x) {
    if (!k) return 0;
    int l=1,r=k+1,mid=k/2;
    while (l+1<r)
    if (calar(x,mid-1)<calar(x,mid)) { l=mid;mid=(l+r)/2; }
    else { r=mid;mid=(l+r)/2; }
    return calar(x,l);
}
void addr(ll l,ll tot) {
    while (k>1&&(d[k].tot-d[k-1].tot)*(d[k-1].l-l)<=(tot-d[k-1].tot)*(d[k-1].l-d[k].l)) k--;
    k++;d[k].l=l;d[k].tot=tot;
}
int main()
{
    int i,n;
    ll ans=0,mx=0;
    scanf("%d", &n);
    for (i=1;i<=n;i++) scanf("%I64d", &a[i]);
    sum[0]=0;k=0;
    for (i=1;i<=n;i++) {
        ans+=(ll)a[i]*i;sum[i]=sum[i-1]+a[i];
    }
    for (i=1;i<=n;i++) {
        mx=max(mx,getchangel(i));
        addl(i,-sum[i-1]);
    }
    k=0;
    for (i=n;i>0;i--) {
        mx=max(mx,getchanger(i));
        addr(i,-sum[i]);
    }
    printf("%I64d\n", ans+mx);
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值