[HDOJ3553 Just a String]【后缀数组搞的像Treap一样的伪后缀树】★★

【题目大意】
一个串的子串定义为其中连续的某一段。
给定一个串,问他的所有子串中,字典序为m的是哪一个子串?
【算法分析】
这个是典型的后缀树问题。只需深度优先遍历一下后缀树就很容易得到这个解。
现在问题来了。。。我不会O(n)构造后缀树。
好吧。那么退一步,我搞个后缀数组。
然后把height建好。然后可以发现。。。其实我们可以在这个搞好的后缀树上模拟后缀树的DFS遍历。。。
好吧,说说这种从Suffix Array->Suffix Tree的沙茶做法。
考虑现在我们已经建好height的ST表,那么我们每次取一个height最小的地方,按这个点划分成两半。然后两半在各自划分下去。
然后现在搞成了一棵heap的关键字是 height ,平衡树的关键字是 某后缀 的“Treap”。
好了。。我们现在可以在这颗“伪后缀树”上搞了。
显然,如果是真的后缀树的话,我们的做法应当是从小到大判断每个叉加起来的子串数量够不够m。然后每次只需选一个叉前进即可。
当然,有可能不前进,那就是后缀树的这个点所压缩的东西,已经足够m了。
然后现在我们来看看这颗伪后缀树和真后缀树的差别。
其实他们所不同的地方就是,假如当前节点有2个以上分叉,
那么后缀树会搞出相等数量的分叉在弄下去。
而对于现在这颗伪后缀树,我们会选择任意一个叉,然后再将它们划分成两边。
然后这样就会造成本身是同深度的叉,变成深度不同了。其实将这些叉合回来就是真后缀树了。
但是对这个题目,我们并不需要把他合回来。现在的信息已经足够我们执行上面红色的算法了。
具体就是搞一个Len_Sum[i]表示后缀数组中前i个后缀的总长度。然后就可以O(1)判断那个叉是对的。然后跑下去。并用cut这个变量表示已经算好了ans的多少长度。
最后有一些琐碎的问题,就是那个后缀树的压缩连续的冗余结点,在这个伪后缀树里体现为假设每次取的那个height最小值!=cut的情况。这时这个伪后缀树中,height最小值-cut,就是那个压缩值的长度。
【其它】2Y。T_T。那个计算要用int64。读入也要。
【CODE】
#include #include #include #include #include using namespace std;
typedef long long lld;
const int N=105555;
int n;
lld m;
char S[N];
char ans[N];
int sa[N];
int lg[N];
int Sum[N];
int rank[N];
int List[N];
int a[N][3];
int b[N][3];
int height[N];
int rmq[18][N];
int pos[18][N];
lld Len_Sum[N];

bool cmp(int i,int j){return S[i]

void Radix_sort(){
     int i,k;
     for (k=1;k>=0;k–){
         for (i=0;i<=n;i++) Sum[i]=0;
         for (i=1;i<=n;i++) Sum[a[i][k]+1]++;
         for (i=1;i<=n;i++) Sum[i]+=Sum[i-1];
         for (i=1;i<=n;i++) memcpy(b[++Sum[a[i][k]]],a[i],12);
         for (i=1;i<=n;i++) memcpy(a[i],b[i],12);
     }
}

void make_suffix_arr(){
     int i,cnt,k,s;
     for (i=1;i<=n;i++) List[i]=i;
     sort(List+1,List+n+1,cmp);
     for (cnt=0,i=1;i<=n;i++)
       rank[List[i]]=( cnt+=(i==1 || S[List[i]]!=S[List[i-1]]) );
     for (k=1;cnt!=n;k++){
         s=1<<(k-1);
         for (i=1;i<=n;i++){
             a[i][0]=rank[i];
             a[i][1]=(i+s>n)?0:rank[i+s];
             a[i][2]=i;
         }
         Radix_sort();
         for (cnt=0,i=1;i<=n;i++)
           rank[a[i][2]]=( cnt+=(i==1 || a[i][0]!=a[i-1][0] || a[i][1]!=a[i-1][1]) );
     }
     for (i=1;i<=n;i++) sa[rank[i]]=i;
}

void make_height(){
     memset(height,0,sizeof(height));
     int i,j,k,st;
     for (i=1;i<=n;i++){
         if (rank[i]==1) continue;
         st=max(0,height[rank[i-1]]-1);
         j=i+st; k=sa[rank[i]-1]+st;
         while (j<=n && k<=n && S[j]==S[k]){j++; k++; st++;}
         height[rank[i]]=st;
     }
}

void build_rmq(){
     int i,k;
     for (i=1;i<=n;i++){
         rmq[0][i]=height[i];
         pos[0][i]=i;
     }
     for (k=1;1<       for (i=1;i+(1<         if (rmq[k-1][i]<=rmq[k-1][i+(1<           rmq[k][i]=rmq[k-1][i];
           pos[k][i]=pos[k-1][i];
         }
         else{
           rmq[k][i]=rmq[k-1][i+(1<           pos[k][i]=pos[k-1][i+(1<         }
}

int Get_Min_Pos(int l,int r){
    if (l>r) swap(l,r);
    int k=lg[r-l+1];
    return rmq[k][l]<=rmq[k][r-(1<}

void Output(int i,int Len){
     int j;
     for (j=0;j       putchar(S[sa[i]+j]);
     putchar(‘n’);
}

void solve(){
     lld cnt=0,cut=0,l=1,r=n,i,res;
     Len_Sum[0]=0;
     for (i=1;i<=n;i++)
       Len_Sum[i]=Len_Sum[i-1]+(n-sa[i]+1);
     while (l           i=Get_Min_Pos(l+1,r);
           res=height[i]-cut;
           if (cnt+res*(r-l+1)>=m){
             Output(l,(m-cnt-1)/(r-l+1)+1+cut);
             return;
           }
           cnt+=res*(r-l+1);
           cut+=res;
           if (Len_Sum[i-1]-Len_Sum[l-1]-(i-l)*cut+cnt>=m)
             r=i-1;
           else{
             cnt+=Len_Sum[i-1]-Len_Sum[l-1]-(i-l)*cut;
             l=i;
           }
     }
     Output(l,m-cnt+cut);
}

int main(){
    int Tc,i;
    lg[1]=0;
    for (i=2;i      if (i==1<                      else lg[i]=lg[i-1];
    scanf("%d",&Tc);
    for (i=1;i<=Tc;i++){
        scanf("%s %I64d",S+1,&m);
        printf("Case %d: ",i);
        n=strlen(S+1);
        make_suffix_arr();
        make_height();
        build_rmq();
        solve();
    }
}

[HDOJ3545 Board Coloring]【动态规划】【四维求和】★★

【题目大意】
给定一个4*n的矩阵a[i,j]。(1<=i<=4 1<=j<=n)你要在里面填0~255的数字。
然后使得填了以后满足以下约束:
1:a[i,j]>=a[i,j-1]
2:对于一些给定了限制(i,j,i’,j’),必须满足a[i,j]=a[i’,j’]。
【算法分析】
非常非常好的题目,没有涉及到什么高深的算法,但是非常巧妙,对于锻炼思维能力有很好的效果。
令F[p[1]][p[2]][p[3]][p[4]]表示现在涂完颜色c,然后第i列涂到p[i]时的方案数。
然后很容得出F[p[1]][p[2]][p[3]][p[4]]=Sum{F[c-1][p[1]’][p[2]’][p[3]’][p[4]’]}。
其中p[i]’<=p[i]。
然后对于那些相等限制,就是假如某个状态(p[i]但是这个求和还是不太简单。
一开始我搞了3个辅助数组一起求T_T。。。
后来猛然发现本质就是在“固定”其它3个位,只留现在这个位来动。然后就想到一个一个位顺着弄。
就变成现在程序这样子。
实际上是不需要滚动数组的。因为我们每次求出的都是下一次所需要的原数组。
【其它】1Y
【Record】时间跑成第一了。可能因为其它人大多用了滚动数组吧。
【CODE】
#include #include #include const int N=16;
const int Mod=100000;
int n,m;
int F[N][N][N][N];
int v[N][N][N][N];

void init(){
     memset(v,0,sizeof(v));
     scanf("%d%d",&n,&m);
     int i,p[5],x1,y1,x2,y2;
     for (i=1;i<=m;i++){
       scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
       for (p[1]=0;p[1]<=n;p[1]++)
         for (p[2]=0;p[2]<=n;p[2]++)
           for (p[3]=0;p[3]<=n;p[3]++)
             for (p[4]=0;p[4]<=n;p[4]++)
               if ( (p[x1]                 v[p[1]][p[2]][p[3]][p[4]]=1;
     }
}

void work(){
     memset(F,0,sizeof(F));
     int p[5],c,i,delta;
     F[0][0][0][0]=1;
     for (c=1;c<=256;c++){
         for (delta=1;delta<=4;delta++)
           for (p[1]=0;p[1]<=n;p[1]++)
             for (p[2]=0;p[2]<=n;p[2]++)
               for (p[3]=0;p[3]<=n;p[3]++)
                 for (p[4]=0;p[4]<=n;p[4]++)
                   if (p[delta]){
                     int &t=F[p[1]][p[2]][p[3]][p[4]];
                     p[delta]–;
                     t    +=F[p[1]][p[2]][p[3]][p[4]];
                     p[delta]++;
                     if (t>=Mod) t-=Mod;
                   }
         for (p[1]=0;p[1]<=n;p[1]++)
           for (p[2]=0;p[2]<=n;p[2]++)
             for (p[3]=0;p[3]<=n;p[3]++)
               for (p[4]=0;p[4]<=n;p[4]++)
                 if (v[p[1]][p[2]][p[3]][p[4]])
                   F[p[1]][p[2]][p[3]][p[4]]=0;
     }
     printf("%05dn",F[n][n][n][n]);
}

int main(){
    int Tc,i;
    scanf("%d",&Tc);
    for (i=1;i<=Tc;i++){
        printf("Case %d: ",i);
        init();
        work();
    }
    return 0;
}

[SGU110 Dungeon]【计算几何】【模拟】【球面反射】

【题目大意】
给定n个球,与一条射线,然后让你模拟这个射线在这些球之间弹,输出弹到得球的标号。如果弹到<=10个就有多少输出多少。否则,输出前10个+ 'etc.'

【算法分析】
我们用三维向量来解决。
1、首先根据点到球心距离等于半径 + 我们的运动向量列出一个方程,解这个一元二次方程可以得到我们什么时候碰到这个球。
2、然后碰到以后呢。就把射线起点,球上的碰触点,球心这三个点,搞成一个平面,然后再在上面以向量:碰触点->球心作为法线,考虑镜面反射即可。
然后这里我再次用了一个解方程+向量伸缩的方法得到了他反射以后的向量。

关于第二步的反射部分,我们所知道的条件有:入射向量,法线向量。我的做法具体就是先构造出向量tmp=发现向量-入射向量。并使得|tmp|=|入射向量|。然后就可以通过一些几何知识来搞了。

【其它】WA了几遍,因为我解一元二次方程的时候判断无解写得是delta<1e-6,然后如果1e-6<=delta<0的话,我就先将它置为0再进行后面的计算。
我改成delta<0就为无解就AC了。T_T。。。精度什么的,最讨厌了。

【CODE】
#include #include #include #include #include #define dis(A,B) sqrt( (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)+(A.z-B.z)*(A.z-B.z) )
#define Sqr(x) ((x)*(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;
const int N=1005;
const double eps=1e-6;
int n;
double res1,res2;
struct Circle{double x,y,z,r;}a[N];
struct Point{double x,y,z;}p,fx;

void init(){
     scanf("%d",&n);
     int i,j;
     for (i=1;i<=n;i++)
       cin >> a[i].x >> a[i].y >> a[i].z >> a[i].r;
     cin >> p.x >> p.y >> p.z >> fx.x >> fx.y >> fx.z;
     fx.x-=p.x;
     fx.y-=p.y;
     fx.z-=p.z;
}

Point Get_Point(double rate){
      Point res;
      res.x=fx.x*rate+p.x;
      res.y=fx.y*rate+p.y;
      res.z=fx.z*rate+p.z;
      return res;
}

double Get_Meet_Rate(Circle Cir){
       double a,b,c,delta,x1,x2;
       Point tmp;
       tmp.x=Cir.x-p.x;
       tmp.y=Cir.y-p.y;
       tmp.z=Cir.z-p.z;
       a=Sqr(fx.x)+Sqr(fx.y)+Sqr(fx.z);
       b=-2*(fx.x*tmp.x + fx.y*tmp.y + fx.z*tmp.z);
       c=-Sqr(Cir.r)+Sqr(tmp.x)+Sqr(tmp.y)+Sqr(tmp.z);
       delta=b*b-4*a*c;
       if (delta<0) return 1e22;
       delta=sqrt(delta);
       x1=(-b+delta)/(2*a);
       x2=(-b-delta)/(2*a);
       if (x1<=eps) x1=1e22;
       if (x2<=eps) x2=1e22;
       res1=x1; res2=x2;
       return min(x1,x2);
}

Point opposite(Point tmp){
      Point res;
      res.x=-tmp.x;
      res.y=-tmp.y;
      res.z=-tmp.z;
      return res;
}

void Reflect(int i,double rate){
     Point cross=Get_Point(rate);
     Get_Meet_Rate(a[i]);
     if ( fabs(res1-res2) < eps ){
       p=cross;
       return;
     }
     Point normal_line,tmp;
     normal_line.x=(a[i].x-cross.x)/2;
     normal_line.y=(a[i].y-cross.y)/2;
     normal_line.z=(a[i].z-cross.z)/2;
     double t=Sqr(normal_line.x)+Sqr(normal_line.y)+Sqr(normal_line.z);
     t/=(normal_line.x*fx.x + normal_line.y*fx.y + normal_line.z*fx.z);
     fx.x*=t; fx.y*=t; fx.z*=t;
     normal_line.x*=2;
     normal_line.y*=2;
     normal_line.z*=2;
    
     tmp.x=normal_line.x-fx.x;
     tmp.y=normal_line.y-fx.y;
     tmp.z=normal_line.z-fx.z;
    
     fx=opposite(tmp);
     p=cross;
    
     bool flag=false;
     if (fx.x>10) flag=true;
     if (fx.y>10) flag=true;
     if (fx.z>10) flag=true;
     if (!flag){
       fx.x*=10;
       fx.y*=10;
       fx.z*=10;
     }
}

void work(){
     int i,best,cnt=0;
     double rate,tmp;
     while (1){
           rate=1e20;
           best=0;
           for (i=1;i<=n;i++){
               tmp=Get_Meet_Rate(a[i]);
               if (tmp                 rate=tmp;
                 best=i;
               }
           }
           if (best==0) break;
           Reflect(best,rate);
           cnt++;
           if (cnt>10) break;
           if (cnt!=1) cout << " ";
           cout << best;
     }
     if (cnt>10) cout << " etc." << endl;
            else cout << endl;
}

int main(){
    init();
    work();
    return 0;
}

[HDOJ2966 In case of failure]【平面每个点的最近点对】【暴力T_T】

【题目大意】
给定n个点,求每个点到离他最近点的距离。
【算法分析】
V图什么的,最讨厌了。。。
我直接就暴力了。。。
按x把点排序,然后按j递增序,枚举i-j的点和i+j的点。如果(a[i].x-a[i-j].x)^2 > dis && (a[i].x-a[i+j].x)^2 > dis
那么就break吧。
复杂度还是n^2的。
其中dis为之前枚举所得的较优解。
【其它】果断排到超级后面。
叉姐说,见到题目要先水一水,水不过再切。。。= =
【CODE】
#include #include #include #include #define dis(A,B) ( (lld)(A.x-B.x)*(lld)(A.x-B.x) + (lld)(A.y-B.y)*(lld)(A.y-B.y) )
#define Sqr(x) ((lld)(x)*(lld)(x))
using namespace std;
typedef long long lld;
const lld INF=(lld)(2000000000)*(lld)(2000000000);
int Tc;
int n;
struct Point{int x,y,pos; lld dis;}a[105555];

bool cmp(Point A,Point B){
return A.x}

void init(){
scanf("%d",&n);
int i;
for (i=1;i<=n;i++){
scanf("%d%d",&a[i].x,&a[i].y);
a[i].pos=i;
}
sort(a+1,a+n+1,cmp);
}

void work(){
int i,j;
bool flag;
lld tmp;
for (i=1;i<=n;i++){
a[i].dis=INF;
for (j=1;(i-j>=1 || i+j<=n);j++){
flag=false;
if (i-j>=1 && Sqr(a[i].x-a[i-j].x) tmp=dis(a[i],a[i-j]);
if (tmp flag=true;
}
if (i+j<=n && Sqr(a[i+j].x-a[i].x) tmp=dis(a[i],a[i+j]);
if (tmp flag=true;
}
if (!flag) break;
}
}
}

bool cmp2(Point A,Point B){
return A.pos}

void output(){
sort(a+1,a+n+1,cmp2);
for (int i=1;i<=n;i++)
printf("%I64dn",a[i].dis);
}

int main(){
scanf("%d",&Tc);
for (int i=0;i init();
work();
output();
}
}

[SGU108 Self-numbers 2]【枚举】【常数题】

【算法分析】

就是类似筛法那样弄下去。但是特殊的是i只会影响到一个>i的数。

而且之多+6*9。所以,我们可以开滚动数组来弄。(开10^7 bool会MLE)

然后模拟高精度那样+1。就可以很方便地维护该数所指的下一个数next。

【其它】速度貌似不错。180+MS。

【Record】

1057066 13.08.10 12:37 edward 108 .CPP Accepted 186 ms 43 kb

【CODE】

#include