@Scarlet
        
        2017-01-12T12:07:21.000000Z
        字数 6719
        阅读 3931
    POI 2006 OI 解题报告
题意:查询数组中最前面的小于的数的位置,要求答案递减(详见题面)
维护前缀min,单调扫一遍。
/*Author:Scarlet*/#include<bits/stdc++.h>#define maxn 300010using 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 1000010using 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 3005using 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|1int 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 1e9using 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 2020using 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 55using 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)//能取1s^=1,a1=a0+t2*(1<<x)+t1*tt,//本位为0的方案数a2=t1*(1<<x)+t2*tt;//本位为1的方案数else a1=t1*tt,a2=t2*tt;//只能取0a0=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 2001000using 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);}
休息一会儿蛤