BZOJ3612:[Heoi2014]平衡 整数划分

思路:

首先有两种情况,就是在中轴线处放不放,于是问题转化为在两侧分别放一些橡皮,使得距离之和相同.

我们令\(f[i][j]\)表示在一侧放橡皮,距离之和为\(i\),共放了\(j\)个橡皮的可行方案数.

不难转化为整数分解问题,也就是将整数\(i\)分解为\(j\)个不相同正整数,且每个正整数均不超过\(n\)的问题.

我们依旧分情况讨论:

[1]若划分的最小正整数为1,我们拿走最下面一行,转化为\(f[i-j][j-1]\).

[2]否则,我们拿走最下面一行转化为\(f[i-j][j]\).

不过最大的正整数不能超过\(n\),我们发现我们每次都是在最下面加上一行,那么如果加上后不合法,最后一列就应该是\(n+1\),于是不合法的方案数就应该是\(f[i-n-1][j-1]\).

于是\(f[i][j]=f[i-j][j]+f[i-j][j-1]-f[i-n-1][j-1]\)

记忆化搜索水过.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
int n,k,p,lim;
 
int f[100010][11];
 
int Solve(int i,int j){
    if(j==1)return(i>0&&i<=n)?1:0;
    if(i<=0||i<(1+j)*j/2||i>=j*n)return 0;
    if(f[i][j]!=-1)return f[i][j];
    return f[i][j]=((long long)Solve(i-j,j-1)+Solve(i-j,j)-Solve(i-n-1,j-1)+p)%p;
}
int main(){
    int T;cin>>T;
    register int i,j;
    while(T--){
        scanf("%d%d%d",&n,&k,&p);
        if(k==1){puts("1");continue;}
        memset(f,-1,sizeof f);
        for(i=0;i<=n*k;++i)f[i][0]=0;
        for(i=1;i<=n*k;++i)for(j=1;j<=k;++j)f[i][j]=Solve(i,j);//printf("n=%d m=%d t=%d\n",i,j,f[i][j]);
        long long res=0;
        for(i=1;i<=n*k;++i){
            for(j=1;j<k;++j){
                res=(res+(long long)f[i][j]*f[i][k-j])%p;
                res=(res+(long long)f[i][j]*f[i][k-j-1])%p;
            }
        }
        printf("%d\n",res);
    }return 0;
}

Codeforces77E Martian Food 计算几何+圆的反演

思路:

设两个比较小的圆分别是A,B.

我们将最大圆以及其中一个圆A的交点取出,并以这个点为反演中心,以合适的长度为反演半径,进行反演.

容易发现大圆和A反演之后都成了一条直线.而圆B由于与这两个圆都有唯一交点,那么反演之后也一定都有唯一交点,因此只能是夹在两条直线中间.

不妨以反演中心为原点,三个圆的直径为x轴(方向任意)建立平面直角坐标系,显然B反演之后的圆纵坐标为0.

那么如果我们再放上别的圆(假设一直放在上方),由于它与这三个圆都有唯一交点,所以依然是被卡在两条直线中间,而且在刚才反演的那个圆上方.

这些圆的大小相同,因此我们可以\(O(1)\)得到第\(k\)个圆的圆心半径.

接下来只要再反演回来就好了.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;

typedef double f2;

struct Point{
    f2 x,y;
    Point(){}
    Point(f2 _x,f2 _y):x(_x),y(_y){}
}P1,P2;
f2 sqr(const f2&x){return x*x;}
f2 dis(const Point&A,const Point&B){return sqrt(sqr(A.x-B.x)+sqr(A.y-B.y));}

inline void GetPointOnDiameter(f2 ox,f2 oy,f2 r,f2 k,f2 b){
    f2 deltax=r/sqrt(k*k+1);
    P1=Point(ox-deltax,k*(ox-deltax)+b);
    P2=Point(ox+deltax,k*(ox+deltax)+b);
}

Point Invert(f2 ox,f2 oy,f2 r,Point A){
    Point o(ox,oy);f2 d=dis(o,A),_d=r*r/d;
    f2 x=ox+(A.x-ox)*(_d/d);
    f2 y=oy+(A.y-oy)*(_d/d);
    return Point(x,y);
}

int main(){
    int Testcase;cin>>Testcase;
    f2 R,r,lx,rx,invr,ox,oy,totalr;int k;
    while(Testcase--){
        scanf("%lf%lf%d",&R,&r,&k);
        invr=r/2;
        rx=invr*invr/(2*r);
        lx=invr*invr/(2*R);
        totalr=(rx-lx)/2,ox=(lx+rx)/2,oy=2*k*totalr;
        GetPointOnDiameter(ox,oy,totalr,oy/ox,0);
        printf("%.10lf\n",dis(Invert(0,0,invr,P1),Invert(0,0,invr,P2))/2);
    }return 0;
}

 

 

BZOJ3831:[Poi2014]Little Bird dp+单调队列

思路:

一开始是用的线段树优化dp,具体的就不说了.

我一开始觉得有的需要+1,有的不需要加,单调队列没办法做.

不过有一条性质,就是如果两条决策,其中一个的步数已经比另一个决策少,那么我们选择这个决策是一定不劣于另一个决策的.

否则若步数相同,我们当然选择保留高度较高的那个决策.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;

namespace Fio{
    inline int getc(){
        static const int L=1<<15;static char buf[L],*S=buf,*T=buf;
        if(S==T){T=(S=buf)+fread(buf,1,L,stdin);if(S==T)return EOF;}
        return*S++;
    }
    template<typename T>inline void Get(T&x){
        int c;while(!isdigit(c=getc())&&c!='-');bool sig=c=='-';
        x=sig?0:c-'0';while(isdigit(c=getc()))x=(x<<1)+(x<<3)+c-'0';
        if(sig)x=-x;
    }
}
#define N 1000010
int h[N];
int q[N],fr,ta,f[N];
int main(){
	int n;Fio::Get(n);register int i,j;for(i=1;i<=n;++i)Fio::Get(h[i]);
	int Q;Fio::Get(Q);int lim;
	while(Q--){
		 Fio::Get(lim);
		 fr=ta=0;q[ta++]=1;f[1]=0;
		 for(i=2;i<=n;++i){
		 	while(fr^ta&&i>q[fr]+lim)++fr;f[i]=f[q[fr]]+(h[q[fr]]<=h[i]);
		 	while(fr^ta&&((f[i]<f[q[ta-1]])||(f[i]==f[q[ta-1]]&&h[i]>h[q[ta-1]])))--ta;
		 	q[ta++]=i;
		 }
		 printf("%d\n",f[n]);
	}return 0;
}

BZOJ3834:[Poi2014]Solar Panels 分块

思路:

首先若\(x\)可以作为答案,必定满足\(b/i-(a-1)/i\geq{1},d/i-(c-1)/i\geq{1}\).

注意到单独的\(v/i\)是只有\(O(\sqrt{n})\)块的,我们可以单独算出对于\(a,b\)满足条件的块,以及对于\(c,d\)满足条件的块.

接下来我们只需求出 两部分的最大交点即可,我们可以在\(O(\sqrt{n})\)的时间内线性扫描得到.

#include<cstdio>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
 
int l[2][100000],r[2][100000],blks[2];
int main(){
    int T;cin>>T;int a,b,c,d,last,ans;register int i,j,k;
    while(T--){
        scanf("%d%d%d%d",&a,&b,&c,&d);blks[0]=blks[1]=0;
        for(i=1;i<a;i=last+1){
            last=min(b/(b/i),(a-1)/((a-1)/i));
            if(b/i-(a-1)/i>=1)l[0][++blks[0]]=i,r[0][blks[0]]=last;
        }
        l[0][++blks[0]]=a,r[0][blks[0]]=b;
        for(i=1;i<c;i=last+1){
            last=min(d/(d/i),(c-1)/((c-1)/i));
            if(d/i-(c-1)/i>=1)l[1][++blks[1]]=i,r[1][blks[1]]=last;
        }
        l[1][++blks[1]]=c,r[1][blks[1]]=d,
        j=1;
        for(ans=0,i=1;i<=blks[0];++i){
            while(j<=blks[1]&&r[1][j]<l[0][i])++j;
            for(k=j;k<=blks[1]&&l[1][k]<=r[0][i];++k)ans=max(ans,min(r[0][i],r[1][k]));
        }
        printf("%d\n",ans);
    }return 0;
}

BZOJ2476:战场的数目 矩阵乘法+整数划分

思路:

首先注意到不允许出现矩形,而这个条件很难限制,不过我们可以将矩形也一起算进去,在最后减掉,因为矩形的数目是可以\(O(1)\)计算的.

接下来就是考虑令\(f_i\)表示周长为\(i\)的合法方案数.

我们考虑几种情况:

[1]左侧有一个高度为1的方块

[2]右侧有一个高度为1的方块

[3]左右两侧均没有一个高度为1的方块

对于[1][2],我们可以分别在左侧和右侧拿走一个方块使得周长减少2,对于[3],我们可以拿走下面的一层使得周长减少2.不难发现这些关系都是一一对应的.

下面考虑一个问题,如果一种形态是两侧均有一个方块,那么我们事实上是算了两遍的.所以我们需要减去这种情况.而显然这种情况与周长减少了4的情况一一对应.

因此我们有:\(f_i=3f_{i-2}-f_{i-4}\)

利用矩阵乘法即可.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
 
typedef long long LL;
static const int mod=987654321;
struct Matrix{
    int w,h;LL d[2][2];
    Matrix(int _w,int _h):w(_w),h(_h){memset(d,0,sizeof d);}
    inline void operator*=(const Matrix&B){
        Matrix C(w,B.h);
        register int i,j,k;
        for(i=0;i<C.w;++i)
            for(j=0;j<C.h;++j)
                for(k=0;k<h;++k)
                    C.d[i][j]=(C.d[i][j]+d[i][k]*B.d[k][j])%mod;
        *this=C;
    }
};
 
int main(){
    int n;
    while(scanf("%d",&n)&&n){
        if(n<=3||n&1){puts("0");continue;}
        int t=n/2-1,res;
        if(t==1)res=1;else if(t==2)res=2;
        else{
            Matrix ans(1,2);ans.d[0][0]=1,ans.d[0][1]=2;
            Matrix add(2,2);add.d[0][0]=0,add.d[0][1]=-1,add.d[1][0]=1,add.d[1][1]=3;
            Matrix one(2,2);one.d[0][0]=one.d[1][1]=1;
            t-=2;for(;t;t>>=1,add*=add)if(t&1)one*=add;
            ans*=one;
            res=ans.d[0][1];
        }
        res=((LL)res-(n/2-1)+mod)%mod;
        printf("%d\n",res);
    }return 0;
}

BZOJ2508:简单题 拉格朗日乘数法

思路:考虑若给定一个定点\((x_0,y_0)\),以及某条直线\(ax+by+c=0\),那么距离的平方为:

\[\frac{(ax_0+by_0+c)^2}{a^2+b^2}\]

那么对于若干条直线,我们总能将距离的平方和表示为如下的式子:

\[Ax_0^2+By_0^2+Cx_0y_0+Dx_0+Ey_0+F\]

这是一个无限制求函数最值的问题,我们利用拉格朗日乘数法,搞出两个求导后的式子,令他们均为0,然后带入这个方程就行了.计算的复杂度为\(O(1)\).

容易发现增删直线对于系数的更改都是\(O(1)\)的.

于是我们就在\(O(m)\)的时间内解决了.

(对于系数的判断很"简单")

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
    
typedef double f2;
    
#define N 120010
f2 a[N],b[N],c[N],A,B,C,D,E,F;
   
void print(f2 x){
    if(fabs(x)<1e-6)puts("0.00");else printf("%.2lf\n",fabs(x));
}
 
f2 G[3][4],ansx,ansy;
inline void Solve(){
    register int i,j,k;f2 t;int n=2;
    if(fabs(G[1][1])<1e-6){
        if(fabs(G[1][2])<1e-6)ansx=0,ansy=G[2][3]/G[2][2];
        else ansy=G[1][3]/G[1][2],ansx=(G[2][3]-G[2][2]*ansy)/G[2][1];
        return;
    }
    if(fabs(G[2][2])<1e-6){
        if(fabs(G[2][1])<1e-6)ansx=G[1][3]/G[1][1],ansy=0;
        else ansx=G[2][3]/G[2][1],ansy=(G[1][3]-G[1][1]*ansx)/G[1][2];
        return;
    }
    t=-G[2][1]/G[1][1];G[2][1]=0,G[2][2]+=t*G[1][2],G[2][3]+=t*G[1][3];
    G[2][3]=(fabs(G[2][2])<1e-6)?0:G[2][3]/G[2][2],G[1][3]-=G[1][2]*G[2][3],G[1][3]/=G[1][1];
    ansx=G[1][3],ansy=G[2][3];
}
 
int main(){
    f2 x0,y0,x1,y1,t;int num=0,cnt,now=0;
    int q,qte,del;scanf("%d",&q);
    while(q--){
        scanf("%d",&qte);
        if(qte==0){
            scanf("%lf%lf%lf%lf",&x0,&y0,&x1,&y1);cnt=++num;++now;
            a[cnt]=y0-y1,b[cnt]=x1-x0,c[cnt]=y0*(x0-x1)-x0*(y0-y1),t=a[cnt]*a[cnt]+b[cnt]*b[cnt];
            A+=a[cnt]*a[cnt]/t,B+=b[cnt]*b[cnt]/t;C+=2*a[cnt]*b[cnt]/t;
            D+=2*a[cnt]*c[cnt]/t,E+=2*b[cnt]*c[cnt]/t,F+=c[cnt]*c[cnt]/t;
        }
        else if(qte==1){
            scanf("%d",&del);cnt=del,t=a[cnt]*a[cnt]+b[cnt]*b[cnt],--now;
            A-=a[cnt]*a[cnt]/t,B-=b[cnt]*b[cnt]/t,C-=2*a[cnt]*b[cnt]/t;
            D-=2*a[cnt]*c[cnt]/t,E-=2*b[cnt]*c[cnt]/t,F-=c[cnt]*c[cnt]/t;
        }
        else{
            if(now==0){puts("0.00");continue;}
            G[1][1]=2*A,G[1][2]=C,G[1][3]=-D,G[2][1]=C,G[2][2]=2*B,G[2][3]=-E;
            Solve();
            print(A*ansx*ansx+B*ansy*ansy+C*ansx*ansy+D*ansx+E*ansy+F);
        }
    }
    return 0;
}

BZOJ2876:[Noi2012]骑行川藏 拉格朗日乘数法

思路:套用拉格朗日乘数法在限制下函数最值的方法,添加未知数\(\lambda\),不过限制是什么呢?

显然能量应该满足限制.在最优意义下不难发现能量应该全部耗尽.于是需要我们最优化的函数如下:

\[min{\sum_{i=1}^{n}\frac{s_i}{v_i}+\lambda(\sum_{i=1}^{n}k_i(v_i-v_i')^2{s_i}-E)}\]

对于每个\(v_i\)求导,得到如下等式:

\[2\lambda{k_i}v_i^2(v_i-v_i')=1\]

对于\(\lambda\)求导,有:

\[\sum_{i=1}^{n}k_i(v_i-v_i')^2{s_i}=E\]

首先显然合理的\(v_i\)都应该满足\(v_i>v_i'\),我们考虑给定\(\lambda\)时,我们能够通过二分又第一种限制解出每个\(v_i\).

那么\(\lambda\)是什么?考虑第二种限制,显然左面的值是关于\(\lambda\)单调变化的,我们解出所有\(v_i\)check一下就可以通过二分解出\(\lambda\)的值了.

总之,二分套二分,时间复杂度\(O(nlog^2{n})\).

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
 
typedef double f2;
 
#define N 10010
f2 s[N],k[N],_v[N];
 
f2 solve(f2 lam,int i){
    f2 L=_v[i],R=1e10,mid;
    while(R-L>1e-12){
        mid=(L+R)/2;
        if(2*lam*k[i]*mid*mid*(mid-_v[i])<1)L=mid;else R=mid;
    }return L;
}
int main(){
    int n;f2 E;scanf("%d%lf",&n,&E);
    register int i;for(i=1;i<=n;++i)scanf("%lf%lf%lf",&s[i],&k[i],&_v[i]);
     
    f2 llam=0,rlam=1e5,mid,add,tmp;
    while(rlam-llam>1e-12){
        mid=(llam+rlam)/2;
        for(add=0,i=1;i<=n;++i)tmp=solve(mid,i),add+=k[i]*s[i]*(tmp-_v[i])*(tmp-_v[i]);
        if(add>E)llam=mid;else rlam=mid;
    }
     
    f2 ans=0;for(i=1;i<=n;++i)ans+=s[i]/solve(llam,i);
     
    printf("%.10lf",ans);
     
    return 0;
}

BZOJ2597:[Wc2007]剪刀石头布 补集转化+费用流

思路:注意到三元环不好求,于是转化为所有三元组的数量减去不能形成环的三元组的数量.

我们令\(i\)输给\(j\),便有一条\(i\rightarrow{j}\)的有向边.

考虑不能形成三元环的三元组,则三个点之中必然有一个点入度为\(2\).

那么不能形成环的所有三元组的数量就等于:

\[add=\sum_{i=1}^{n}C_{indegree_{i}}^{2}\]

这意味着:对于所有入边,我们随意找出两个,都显然可以形成一个不能形成环的三元组.而且这样计算显然是不重不漏的.

考虑我们的答案:

\[ans=C_{n}^{3}-add=C_{n}^{3}-\sum_{i=1}^{n}\frac{indegree_{i}(indegree_{i}-1)}{2}\]

那么我们事实上是要最小化后面减去的东西.

我们对于每一场结果未知的比赛分配胜负,若令一个人取胜,那么其入度\(+1\),就会带来总答案的增加,我们发现每一次增广从某个节点通过的费用是单调不减的,于是拆边作费用流即可.

输出方案也就随便搞搞.

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
  
#define INF 0x3f3f3f3f
  
#define N 110
int G[N][N];
  
queue<int>q;
struct Solver{
    int head[5100],next[50010],end[50010],flow[50010],cost[50010],ind;
    int d[5100],used[5100],slack[5100],id;bool inq[5100];
    inline void reset(){ind=0,id=1,memset(head,-1,sizeof head);}
    inline void addedge(int a,int b,int f,int c){int q=ind++;end[q]=b,next[q]=head[a],head[a]=q,flow[q]=f,cost[q++]=c;}
    inline void make(int a,int b,int f,int c){addedge(a,b,f,c),addedge(b,a,0,-c);}
    inline void spfa(int S,int T){
        memset(d,0x3f,sizeof d),d[S]=0,inq[S]=1,q.push(S);
        while(!q.empty()){
            int i=q.front();q.pop(),inq[i]=0;
            for(int j=head[i];j!=-1;j=next[j])if(flow[j]&&d[end[j]]>d[i]+cost[j]){
                d[end[j]]=d[i]+cost[j];
                if(!inq[end[j]])inq[end[j]]=1,q.push(end[j]);
            }
        }for(int i=S;i<=T;++i)d[i]=d[T]-d[i];
    }
    inline bool Newlabel(int S,int T){
        int Min=INF;for(int i=S;i<=T;++i)if(used[i]!=id&&slack[i]<Min)Min=slack[i];
        if(Min==INF)return 0;
        for(int i=S;i<=T;++i)if(used[i]==id)used[i]=-1,d[i]+=Min;return 1;
    }
    int Getflow(int p,int T,int maxflow){
        if(p==T)return maxflow;
        used[p]=id;
        for(int j=head[p];j!=-1;j=next[j]){
            if(flow[j]&&used[end[j]]!=id&&d[end[j]]+cost[j]==d[p]){
                int to=Getflow(end[j],T,maxflow<flow[j]?maxflow:flow[j]);if(to){flow[j]-=to,flow[j^1]+=to;return to;}
            }else if(flow[j])slack[end[j]]=min(slack[end[j]],d[end[j]]+cost[j]-d[p]);
        }return 0;
    }
    int Mincost(int S,int T){
        int res(0),get;spfa(S,T);
        do{
            memset(slack,0x3f,sizeof slack);
            while((get=Getflow(S,T,INF)))res+=d[S]*get,++id;
        }while(Newlabel(S,T));return res;
    }
}InuyashaKikyou;
  
#define Make InuyashaKikyou.make
int win[N],lose[N];
int main(){
    int n;scanf("%d",&n);
    register int i,j;for(i=1;i<=n;++i)for(j=1;j<=n;++j)scanf("%d",&G[i][j]);
    int id=0,t=0;for(i=1;i<=n;++i)for(j=i+1;j<=n;++j)if(G[i][j]==2)++id;
    int S=0,T=n+id+1;
    InuyashaKikyou.reset();
    for(i=1;i<=n;++i)for(j=i+1;j<=n;++j){
        if(G[i][j]==1)++win[i],++lose[j];else if(G[i][j]==0)++win[j],++lose[i];
        else Make(S,++t,1,0),Make(t,id+i,1,0),Make(t,id+j,1,0);
    }
    int nowcnt=0;
    for(i=1;i<=n;++i){
        int last=n-1-win[i]-lose[i];nowcnt+=win[i]*(win[i]-1)/2;
        while(last--)Make(id+i,T,1,win[i]++);
    }
    nowcnt+=InuyashaKikyou.Mincost(S,T);
    printf("%d\n",n*(n-1)*(n-2)/6-nowcnt);
  
    int to[2],last[2];
    for(i=1;i<=id;++i){
        int cnt=0;
        for(j=InuyashaKikyou.head[i];j!=-1;j=InuyashaKikyou.next[j]){
            if(InuyashaKikyou.end[j]>id)to[cnt]=InuyashaKikyou.end[j]-id,last[cnt++]=InuyashaKikyou.flow[j];
        }
        if(last[0]==0&&last[1]==1)G[to[0]][to[1]]=1,G[to[1]][to[0]]=0;
        else G[to[1]][to[0]]=1,G[to[0]][to[1]]=0;
    }
    for(i=1;i<=n;++i){
        for(j=1;j<=n;++j)printf("%d ",G[i][j]);puts("");
    }
    return 0;
}

BZOJ3012:[Usaco2012 Dec]First! Trie树+Tarjan求强连通分量

思路:一开始的暴力思路是对于每个串,若想使得它成为字典序最小的串,就找到它和任意其他串的从前向后第一个字母不同的位置,并添加限制:这个串的该位置字母的字典序小于其他串的该位置字母的字典序.这样若限制无环,我们就说这个串可以是字典序最小的.

然而这样建图花费的时间太长.

若将所有的串插入一颗Trie树,考虑对于每个串,其需要考虑的限制仅仅是从根节点到串的结尾所对应的节点的路径上的分岔.这样限制数最多为\(O(L*26)\),那么总的限制数最多为\(O(300000*26)\),就可以暴力建图并利用TarjanCheck了.

(为什么没想到Trie树..)

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;

#define N 30010
#define L 300010

int ch[L][26],cnt,isend[L];
char s[L];int begin[N],len[N];

int G[26][26];
inline void reset(){memset(G,0,sizeof G);}
inline void addedge(int a,int b){/*printf("%d %d\n",a,b);*/G[a][b]=1;}

int dfn[26],low[26],tclock,stk[26],top,num;bool instk[26];
void dfs(int x){
	dfn[x]=low[x]=++tclock;stk[++top]=x,instk[x]=1;
	for(int j=0;j<26;++j)if(G[x][j]){
		if(!dfn[j])dfs(j),low[x]=min(low[x],low[j]);
		else if(instk[j])low[x]=min(low[x],dfn[j]);
	}if(dfn[x]==low[x]){
		++num;
		while(1){int i=stk[top--];instk[i]=0;if(x==i)break;}
	}
}
inline bool judge(){
	memset(dfn,0,sizeof dfn);tclock=top=num=0;memset(instk,0,sizeof instk);
	for(int i=0;i<26;++i)if(!dfn[i])dfs(i);
	return num==26;
}

int ans[N],siz;
int main(){
	int n;cin>>n;
	register int i,j,k;
	int now=0;for(i=1;i<=n;++i)begin[i]=now,scanf("%s",s+now),len[i]=strlen(s+now),now+=len[i];
	int p,y;
	for(i=1;i<=n;++i){
		for(p=0,j=0;j<len[i];++j){if(!ch[p][y=s[begin[i]+j]-'a'])ch[p][y]=++cnt;p=ch[p][y];}
		isend[p]=i;
	}
	for(i=1;i<=n;++i){
		bool notok=0;
		for(reset(),p=0,j=0;j<len[i];p=ch[p][y],++j){
			if(isend[p]&&isend[p]!=i)notok=1;
			y=s[begin[i]+j]-'a';for(k=0;k<26;++k)if(k!=y&&ch[p][k])addedge(y,k);
		}
		if(!notok&&judge())ans[++siz]=i;
	}
	printf("%d\n",siz);
	//for(i=1;i<=siz;++i)printf("%d\n",ans[i]);
	for(i=1;i<=siz;++i){for(j=0;j<len[ans[i]];++j)putchar(s[begin[ans[i]]+j]);puts("");}
	return 0;
}

2015.01.04集训总结

Task#1 Contest

面对三道傻逼题本沙茶妥妥爆零.大家一起喊出来我弱不弱,弱不弱?

T1题目大意:傻逼题,大家通过代码来猜测什么题目吧.(多带了一个\(log\)结果没有超时,反而由于空间问题被卡了2MB内存,艹!)

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;

#define N 100010
struct Node{
	int l,r,siz;
}S[N*20];int cnt;
int Newv(int last,int tl,int tr,int ins){
	int q=++cnt;S[q]=S[last],++S[q].siz;if(tl==tr)return q;
	int mid=(tl+tr)>>1;
	if(ins<=mid)S[q].l=Newv(S[last].l,tl,mid,ins);else S[q].r=Newv(S[last].r,mid+1,tr,ins);
	return q;
}
int Query(int q,int tl,int tr,int dl,int dr){
	if(dl<=tl&&tr<=dr)return S[q].siz;
	int mid=(tl+tr)>>1;
	if(dl>mid)return Query(S[q].r,mid+1,tr,dl,dr);
	else if(dr<=mid)return Query(S[q].l,tl,mid,dl,dr);
	else return Query(S[q].l,tl,mid,dl,mid)+Query(S[q].r,mid+1,tr,mid+1,dr);
}
struct Case{
	int x,y;
	bool operator<(const Case&B)const{return x<B.x;}
}C[N];
int p[N],q[N],root[N];
int main(){
	freopen("permutation.in","r",stdin),freopen("permutation.out","w",stdout);
	int n;cin>>n;register int i,j;
	for(i=1;i<=n;++i)scanf("%d",&p[i]),C[p[i]].x=i;for(i=1;i<=n;++i)scanf("%d",&q[i]),C[q[i]].y=i;
		sort(C+1,C+n+1);
	for(i=1;i<=n;++i)root[i]=Newv(root[i-1],1,n,C[i].y);
	int lans=0,a,b,c,d,aa,bb,cc,dd,q;scanf("%d",&q);
	while(q--){
		scanf("%d%d%d%d",&a,&b,&c,&d);
		aa=(a-1+lans)%n+1,bb=(b-1+lans)%n+1,cc=(c-1+lans)%n+1,dd=(d-1+lans)%n+1;
		a=min(aa,bb),b=max(aa,bb),c=min(cc,dd),d=max(cc,dd);
		printf("%d\n",(lans=Query(root[b],1,n,c,d)-Query(root[a-1],1,n,c,d)+1)-1);
	}return 0;
	fclose(stdin),fclose(stdout);return 0;

}

T2:见下面的链接

http://wyfcyx.is-programmer.com/posts/75155.html

T3:见下面的链接

http://wyfcyx.is-programmer.com/posts/75156.html

 

Task#2 后缀数组及其应用

现在没时间,等我找到特别有意思的题目来做吧~~~