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;
}