[2012年四川省选]简要题解
来源:互联网 发布:微店和淘宝哪个好做 编辑:程序博客网 时间:2024/04/28 17:54
奇怪的游戏
http://www.lydsy.com/JudgeOnline/problem.php?id=2756
题解
似乎这个题我做过?
这是一道非常不错的黑白染色的最大流建模题。。。
首先按照惯例对整个棋盘进行黑白染色,不妨设
得到
因此在
下面问题变成了已知
这就是一个黑白染色的最大流模型,我们让源点
另外注意Dinic的一些优化,不优化会TLE。
代码
#include <iostream>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <algorithm>#define MAXE 21000#define MAXV 2100#define INF 0x3f3f3f3f3f3f3f3fusing namespace std;typedef long long int LL;int n,m;int S,T;struct edge{ LL cap; int u,v,next;}edges[MAXE];int head[MAXV],nCount=1;inline void AddEdge(int U,int V,LL C){ edges[++nCount].u=U; edges[nCount].v=V; edges[nCount].cap=C; edges[nCount].next=head[U]; head[U]=nCount;}inline void add(int U,int V,LL C){ AddEdge(U,V,C); AddEdge(V,U,0);}int q[MAXE];int layer[MAXV];inline bool CountLayer(){ memset(layer,-1,sizeof(layer)); int h=0,t=1; q[h]=S; layer[S]=0; while(h<t) { int u=q[h++]; for(int p=head[u];p!=-1;p=edges[p].next) { int v=edges[p].v; if(edges[p].cap&&layer[v]==-1) { layer[v]=layer[u]+1; q[t++]=v; } } } return layer[T]!=-1;}LL DFS(int u,LL flow){ if(u==T) return flow; //!!!!! LL used=0; for(int p=head[u];p!=-1;p=edges[p].next) { int v=edges[p].v; if(layer[v]==layer[u]+1) { LL tmp=DFS(v,min(flow-used,edges[p].cap)); used+=tmp; edges[p].cap-=tmp; edges[p^1].cap+=tmp; if(used==flow) return flow; } } if(!used) layer[u]=-1; return used;}inline LL Dinic(){ LL maxflow=0; while(CountLayer()) maxflow+=DFS(S,INF); return maxflow;}int a[MAXV][MAXV];int color[MAXV][MAXV];inline int calc(int x,int y){ return (x-1)*m+y;}int xx[]={1,-1,0,0},yy[]={0,0,1,-1};inline bool check(LL x) //检查所有格子都变成数字x是否可能{ S=MAXV-2,T=MAXV-1; nCount=1; memset(head,-1,sizeof(head)); LL sum=0; //!!!!满流的流量 for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(color[i][j]) { add(S,calc(i,j),x-a[i][j]); sum+=x-a[i][j]; for(int dir=0;dir<4;dir++) { int newi=i+xx[dir],newj=j+yy[dir]; if(newi<1||newi>n||newj<1||newj>m) continue; add(calc(i,j),calc(newi,newj),INF); } } else add(calc(i,j),T,x-a[i][j]); } return Dinic()==sum;}int main(){ int T; scanf("%d",&T); while(T--) { LL maxa=0,sum1=0,sum2=0,num1=0,num2=0; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { scanf("%d",&a[i][j]); color[i][j]=(i+j)%2; maxa=max(maxa,(LL)a[i][j]); } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(color[i][j]) { sum1+=a[i][j]; num1++; } else { sum2+=a[i][j]; num2++; } } if(num1!=num2) { if((sum1-sum2)/(num1-num2)>=maxa) if(check((sum1-sum2)/(num1-num2))) { printf("%lld\n",((sum1-sum2)/(num1-num2))*num1-sum1); continue; } puts("-1"); continue; } LL ans,lowerBound=maxa,upperBound=1e18; while(lowerBound<=upperBound) { LL mid=(lowerBound+upperBound)>>1; if(check(mid)) { ans=mid; upperBound=mid-1; } else lowerBound=mid+1; } printf("%lld\n",ans*num1-sum1); } return 0;}
喵星球上的点名
http://www.lydsy.com/JudgeOnline/problem.php?id=2754
题解
来自出题人满满的恶意。。。。
有两种做法:1、AC自动机 2、后缀数组。
AC自动机做法很复杂,因为此题非常丧病地没有限定字符集的大小,这样就导致不能用Trie树传统的保存儿子的方式,只能用map,并且这样会让最终的算法复杂度多一个
而后缀数组的做法就随意了很多,因为SA对字符集没有什么特别的要求,字符串都是可以看成是数字串,非常爽,果断用SA。
SA的做法看上去就是乱搞:首先把所有猫的名的字符串、猫的姓的字符串、点名的字符串拼成一个大的字符串,每个不同的串之间用不同的分割符隔开,注意要不同的分割符,为了避免RP爆零时匹配错误。
然后注意到题目的一个很重要的限制:一个猫被点名,点名串可以是它的姓的子串,也可是它的名的子串,但是绝对不能是它的姓和名拼起来的子串,因此每个猫的姓和名必须分开看待。
另外要注意到SA一个非常关键的性质:对于一些具有相同前缀的后缀来说,他们的排名是挨在一起的,对于一个开头下标为
代码
#include <iostream>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <algorithm>#define MAXN 510000using namespace std;int wa[MAXN],wb[MAXN],cnt[MAXN],wv[MAXN];int rank[MAXN],height[MAXN],sa[MAXN];bool cmp(int *r,int a,int b,int c) //一个元素是(a,a+c),另一个元素是(b,b+c){ return (r[a]==r[b])&&(r[a+c]==r[b+c]);}void SA(int *r,int n,int m) //待排序串为r,长为n(第n项为空,防止cmp函数溢出),后缀数组为sa(sa[i]=排名为i的后缀的开头下标),字母范围在[0,m){ int i,j,p; int *x=wa,*y=wb; //其实就是滚动数组 for(i=0;i<m;i++) cnt[i]=0; //桶清零 for(i=0;i<n;i++) cnt[(x[i]=r[i])]++; //将每位字符放入对应的桶中 for(i=1;i<m;i++) cnt[i]+=cnt[i-1]; //此时cnt[i]=小于等于i的字符个数 for(i=n-1;i>=0;i--) sa[--cnt[x[i]]]=i; for(j=1,p=1;p<n;j*=2,m=p) //处理每个后缀长度为j的前缀的排序,p是这些前缀的个数,m是每次基数排序的关键字最大值 { //先按照第二关键字排一遍序 for(p=0,i=n-j;i<n;i++) y[p++]=i; //把第二关键字为0的那部分元素先放进来 for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; //只有那些开头下标>=j的元素才能当作第二关键字,y[i]=第二关键字排名为i的元素的第一关键字下标 for(i=0;i<n;i++) wv[i]=x[y[i]]; for(i=0;i<m;i++) cnt[i]=0; //桶清零 for(i=0;i<n;i++) cnt[wv[i]]++; for(i=1;i<m;i++) cnt[i]+=cnt[i-1]; //此时cnt[i]=小于等于i的字符个数 for(i=n-1;i>=0;i--) sa[--cnt[wv[i]]]=y[i]; //更新sa数组 swap(x,y); //交换x、y数组,计算新的x数组 for(p=1,x[sa[0]]=0,i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; //更新名次数组x[],x[i]=开头下标为i的后缀的排名。若当前的后缀与之前的后缀排名相同,排名p不能+1,否则排名p要加1 }}void cal(int *r,int n) //此时的n是字符串的实际长度{ int i,j,k=0; for(i=1;i<=n;i++) rank[sa[i]]=i; for(i=0;i<n;height[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);}struct Query{ int l,start; //start=该点名串开始的字符的下标,l=该点名串长度}query[MAXN];int num[MAXN];int belong[MAXN]; //belong[i]=字符串中第i位所属的猫编号int ans[MAXN]; //ans[i]=第i只猫被点名次数int vis[MAXN]; //vis[i]=第i只猫所属的点名串编号,防止重复点名int main(){ int len=0; int n,m; scanf("%d%d",&n,&m); int breaker=11000; for(int i=1;i<=n;i++) //读入第i个猫的姓和名 { int l; scanf("%d",&l); for(int j=1;j<=l;j++) { belong[len]=i; scanf("%d",&num[len++]); } num[len++]=++breaker; //所有的分割符都必须完全不一样,避免错误匹配 scanf("%d",&l); for(int j=1;j<=l;j++) { belong[len]=i; scanf("%d",&num[len++]); } num[len++]=++breaker; //所有的分割符都必须完全不一样,避免错误匹配 } for(int i=1;i<=m;i++) { scanf("%d",&query[i].l); query[i].start=len; for(int j=1;j<=query[i].l;j++) scanf("%d",&num[len++]); num[len++]=++breaker; } SA(num,len,200000); cal(num,len-1); for(int i=1;i<=m;i++) { int L,R; L=R=rank[query[i].start]; while(height[L]>=query[i].l) L--; //区间[L,R]里排名对应的后缀都包含了第i个点名串,它们与以这个点名串的开头在整个拼起来的串的后缀的LCP值,肯定是大于等于这个点名串的长度的 while(height[R]>=query[i].l) R++; R--; int sum=0; //sum=第i次点名点到的猫的个数 for(int j=L;j<=R;j++) //枚举区间[L,R]里每个后缀的前缀对应的是哪些猫 { if(belong[sa[j]]) //排名为j的后缀的开头是属于某个猫的 { if(vis[belong[sa[j]]]!=i) //在第i个点名串的计数过程中还没有算入这只猫 { vis[belong[sa[j]]]=i; sum++; ans[belong[sa[j]]]++; } } } printf("%d\n",sum); } for(int i=1;i<=n;i++) printf("%d%c",ans[i],i==n?'\n':' '); return 0;}
- [2012年四川省选]简要题解
- [2014年四川省选]简要题解
- NOIP2012 简要题解
- noip2013 简要题解
- 一些简要题解
- [BestCoder #31]简要题解
- [BestCoder #33]简要题解
- 弱省胡策系列简要题解
- NOI 2014简要题解
- NOIP 2015 简要题解
- ASC 20简要题解
- JSOI2016 Round2 简要题解
- NOIP2016 简要题解
- hdu4786简要题解
- CodeChef AUG17 简要题解
- CodeForces 843 简要题解
- CodeChef SEPT17 简要题解
- CodeForces 878 简要题解
- eclipse、web2py环境搭建
- jsp 页面嵌java代码(小脚本)
- 3.18
- 数据绑定和模块
- [译]【Storm入门指南】第二章 入门实例
- [2012年四川省选]简要题解
- 黑马程序员--OC-类与对象
- Hello World
- php static function
- 写代码真是一件有意思的事情
- 面试10大问题
- android对文件的MD5验证
- [leecode 190]Reverse digits of an integer.Reverse digits of an integer.
- 【开发平台】git在Android平台Eclipse上的安装和使用