@Scarlet
2017-01-12T20:07:21.000000Z
字数 6719
阅读 3365
POI
2006
OI
解题报告
题意:查询数组中最前面的小于的数的位置,要求答案递减(详见题面)
维护前缀min,单调扫一遍。
/*
Author:Scarlet
*/
#include<bits/stdc++.h>
#define maxn 300010
using namespace std;
typedef long long LL;
#define G c=getchar()
inline int read()
{
int x=0,f=1;char G;
while(c>57||c<48){if(c=='-')f=-1;G;}
while(c>47&&c<58)x=x*10+c-48,G;
return x*f;
}
int a[maxn];
int main()
{
int n=read(),m=read();a[0]=23333333;
for(int i=1;i<=n;i++)a[i]=min(read(),a[i-1]);
int i=n,x;
for(;m--;i--)for(x=read();a[i]<x&&i;i--);
printf("%d",i<0?0:i+1);
return 0;
}
题意:求一个字符串所有前缀的最大周期长度之和,这里的周期长度不一定要整除原长
又是kmp好题辣。
先求next,最后发现指向0之前的那个next就是最长循环节了,感觉不难证明。
/*
Author:Scarlet
*/
#include<bits/stdc++.h>
#define N 1000010
using namespace std;
typedef long long LL;
int n,ne[N];char s[N];
int main()
{
scanf("%d\n%s",&n,s);
ne[0]=-1;int i=0,j=-1;
while(i<n)if(j==-1||s[i]==s[j])ne[++i]=++j;else j=ne[j];
LL ans=0;
for(i=1;i<=n;i++)if(ne[i])while(ne[ne[i]])ne[i]=ne[ne[i]];
for(i=1;i<=n;i++)if(ne[i]!=0)ans+=i-ne[i];
printf("%lld\n",ans);
}
题意:每次在平面内查询,将子矩形内所有值修改为其中最大值。
二维线段树好题,标记永久化一下,每次在最大值上进行查询、修改即可。
代码似乎是改自hzwer的?
/*
Author:Scarlet
*/
#include<bits/stdc++.h>
#define maxn 3005
using namespace std;
typedef long long LL;
#define G c=getchar()
#define CI const int&
inline int read()
{
int x=0,f=1;char G;
while(c>57||c<48){if(c=='-')f=-1;G;}
while(c>47&&c<58)x=x*10+c-48,G;
return x*f;
}
#define ls k<<1
#define rs k<<1|1
int D,S,N,ql,qr,qd,qu;
struct segx{
int v[maxn],tag[maxn];
void add(int k,int l,int r,int x,int y,CI val){
v[k]=max(v[k],val);
if(l==x&&y==r){tag[k]=max(tag[k],val);return;}
int mid=(l+r)>>1;
if(x<=mid)add(ls,l,mid,x,min(mid,y),val);
if(y>mid)add(rs,mid+1,r,max(x,mid+1),y,val);
}
int query(int k,int l,int r,int x,int y){
if(l==x&&y==r)return v[k];
int mid=(l+r)>>1,ans=tag[k];
if(x<=mid)ans=max(ans,query(ls,l,mid,x,min(mid,y)));
if(y>mid)ans=max(ans,query(rs,mid+1,r,max(x,mid+1),y));
return ans;
}
};
struct segy{
segx v[maxn],tag[maxn];
void add(int k,int l,int r,int x,int y,CI val){
v[k].add(1,1,S,qd,qu,val);
if(l==x&&y==r){tag[k].add(1,1,S,qd,qu,val);return;}
int mid=(l+r)>>1;
if(x<=mid)add(ls,l,mid,x,min(mid,y),val);
if(y>mid)add(rs,mid+1,r,max(x,mid+1),y,val);
}
int query(int k,int l,int r,int x,int y){
if(l==x&&y==r)return v[k].query(1,1,S,qd,qu);
int mid=(l+r)>>1,ans=tag[k].query(1,1,S,qd,qu);;
if(x<=mid)ans=max(ans,query(ls,l,mid,x,min(mid,y)));
if(y>mid)ans=max(ans,query(rs,mid+1,r,max(x,mid+1),y));
return ans;
}
}T;
int main()
{
D=read();S=read();N=read();
int d,s,w,x,y;
for(int i=1;i<=N;i++)
{
d=read();s=read();w=read();x=read();y=read();
ql=x+1;qr=x+d;qd=y+1;qu=y+s;
int ans=T.query(1,1,D,ql,qr);
T.add(1,1,D,ql,qr,ans+w);
}
qd=1;qu=S;
printf("%d\n",T.query(1,1,D,1,D));
return 0;
}
见题目
KM好题
/*
Author:lbn187
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 1e9
using namespace std;typedef long long LL;LL ans;
int lx[201],ly[201],slf[201],w[201][201],lk[201],n,k,i,j,x,l,r,c;
bool vx[201],vy[201];
bool find(int x){
vx[x]=1;
for(int y=1;y<=n;y++)if(!vy[y])
if(lx[x]+ly[y]==w[x][y]){
vy[y]=1;
if(lk[y]==0||find(lk[y])){
lk[y]=x;
return 1;
}
}else slf[y]=min(slf[y],lx[x]+ly[y]-w[x][y]);
return 0;
}
int main(){
for(scanf("%d",&n),i=1;i<=n;i++){
lx[i]=-inf;
scanf("%d%d%d%d",&x,&l,&r,&c);
for(j=l;j<=r;j++)w[i][j]=inf-abs(j-x)*c;
}
for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(w[i][j]>lx[i])lx[i]=w[i][j];
for(k=1;k<=n;k++){
memset(slf,63,sizeof(slf));
for(;;){
memset(vx,0,sizeof(vx));memset(vy,0,sizeof(vy));
if(find(k))break;int d=inf;
for(i=1;i<=n;i++)if(!vy[i]&&d>slf[i])d=slf[i];
for(i=1;i<=n;i++){
if(vx[i])lx[i]=lx[i]-d;
if(vy[i])ly[i]=ly[i]+d;else slf[i]=slf[i]-d;
}
}
}
for(i=1;i<=n;i++)ans=ans+w[lk[i]][i];ans=(LL)inf*n-ans;
ans>=inf?puts("NIE"):printf("%lld",ans);
}
题意:一次排版,每行长度不可超过m,n个单词依序排下来,求相邻两行长度差之和的最小值
一看就是DP啦,先要写出这样的一个方程:
f[i][j]=min(f[i][j],f[k][i-1]+abs(s[j]-s[i-1]*2+s[k-1]));
可以利用单调性存两个从大转移最小值和从小转移最小值的数组,就可以在时间内出解了。
题解来自lbn187
#include<bits/stdc++.h>
#define maxn 2020
using namespace std;
int m,n,ans=2e9,k,a[maxn];
int f[maxn][maxn],g[maxn][maxn],h[maxn][maxn];
int main()
{
memset(f,63,sizeof(f));memset(g,63,sizeof(g));memset(h,63,sizeof(h));
g[0][0]=0;
scanf("%d%d",&m,&n);m++;
for(int i=1,j;i<=n;i++)
{
scanf("%d",&a[i]);
a[i]+=a[i-1]+1;
for(j=k=i-1;j>=0&&a[i]-a[j]<=m;j--)
{
for(;a[i]-a[j]>=a[j]-a[k]&&k>=0;k--);
f[i][j]=g[j][k+1]+a[i]-a[j];
if(!j)f[i][j]=0;
if(k>=0)f[i][j]=min(f[i][j],h[j][k]+a[j]-a[i]);
g[i][j]=min(g[i][j+1],f[i][j]+a[j]-a[i]);
}
for(j++;j<=i;j++)
h[i][j]=min(h[i][j-1],f[i][j]+a[i]-a[j]);
}
for(int i=0;i<=n;i++)
ans=min(ans,f[n][i]);
printf("%d",ans);
}
题意:给定数组,问有多少组同时满足
看到异或大概就是数位Dp DarkFantasy了嘛。
怎么按位DP啊。考虑到整数的连续性,如果某一位本来能取1,但你取了0,那么接下去若干位就任意了,这就可以大力计算了
/*
Author:Scarlet
好像还不是很懂,下面的注释是瞎写的
有时间再改进
*/
#include<bits/stdc++.h>
#define maxn 55
using namespace std;
typedef unsigned long long LL;
#define G c=getchar()
inline LL read()
{
LL x=0,f=1;char G;
while(c>57||c<48){if(c=='-')f=-1;G;}
while(c>47&&c<58)x=x*10+c-48,G;
return x*f;
}
int n;LL ans,a[maxn];
void dfs(int x)//x位以前全取最高位的方案数
{
LL t1,t2,tt,s=0,a0=1,a1=0,a2=0;
for(int i=1;i<=n;i++)
{
t1=a1;t2=a2;tt=(a[i]&((1<<x)-1))+1;
if(a[i]>>x&1)//能取1
s^=1,
a1=a0+t2*(1<<x)+t1*tt,//本位为0的方案数
a2=t1*(1<<x)+t2*tt;//本位为1的方案数
else a1=t1*tt,a2=t2*tt;//只能取0
a0=a0*tt;//方案基数
}
if(!s)//还没算完
{
ans+=a2;
if(x)dfs(x-1);
else ans++;
}
else ans+=a1;//算完了
}
int main()
{
n=read();
for(int i=1;i<=n;i++)a[i]=read();
dfs(31);
printf("%llu",ans-1);
return 0;
}
个回文串,问多少个使得也是回文串
先考虑这样一个事实:都是回文串,那么也是回文串。
证明十分简单,我们轻易地发现了,然后就没有然后了。
稍微加强一下可以这样:
是回文串,那么
那么问题就变得十分simple了:给定个串,问有多少
可以先对所有建Trie,然后拿所有S放到Trie上跑,跑的时候拿路上的串和当前的匹配一下就过了。
/*
Author:Scarlet
*/
#include<cstdio>
#include<string>
#include<algorithm>
#define S 131
#define N 2001000
using namespace std;typedef unsigned long long LL;
LL hs[N],pw[N],ha,ans;
int n,i,j,x,now,top,len[N],to[N],sum[N],c[N][26];
string s[N];char ch[N];
int main()
{
for(scanf("%d",&n),i=1;i<=n;i++)
{
scanf("%d%s",&len[i],ch+1);s[i]=ch+1;
for(now=ha=0,j=1;j<=len[i];j++)
{
x=ch[j]-'a';
ha=ha*S+x;
if(!c[now][x])c[now][x]=++top;
now=c[now][x];
}
hs[i]=ha;to[now]=i;sum[now]++;
}
for(pw[0]=1,i=1;i<N;i++)pw[i]=pw[i-1]*S;
for(i=1;i<=n;i++)
for(now=0,j=0;j<len[i];j++)
{
int x=s[i][j]-'a';
now=c[now][x];
if(sum[now]&&hs[to[now]]*pw[len[i]]+hs[i]==hs[i]*pw[j+1]+hs[to[now]])ans+=sum[now]*2;
}
printf("%llu",ans-n);
}
休息一会儿蛤