POI 2015

bzoj 4386

把一个点拆成三个点跑矩乘快速幂,因为要求一个前缀和所以新建一个点,连一个自环,把所有点的第三个点连过去。
因为可能暴long long,所以我用的long double,然后很慢。。。

#include <bits/stdc++.h>
using namespace std;
#define ll long long 
#define ld long double
int n,m;
ll K,ans;
int num[52][8],cnt;
struct matrix
{
    ld w[150][150];
    friend matrix operator * (const matrix &r1,const matrix &r2)
    {
        matrix ret;
        memset(&ret,0,sizeof(ret));
        for(int i=1;i<=cnt;i++)
            for(int j=1;j<=cnt;j++)
                for(int k=1;k<=cnt;k++)
                    ret.w[i][j]+=r1.w[i][k]*r2.w[k][j];
        return ret;
    };
}a[110],b,tmp;
ld cal(matrix &x)
{
    ld ret=0;
    for(int i=1;i<=n;i++)
        ret+=x.w[num[i][3]][cnt];
    return ret-n;
}
int main()
{
    scanf("%d%d%lld",&n,&m,&K);
    for(int i=1;i<=n;i++)
    {   
        for(int j=1;j<=3;j++)
            num[i][j]=++cnt;
        a[0].w[num[i][1]][num[i][2]]++;
        a[0].w[num[i][2]][num[i][3]]++;
    }
    cnt++;
    a[0].w[cnt][cnt]++;
    for(int i=1;i<=n;i++)
        a[0].w[num[i][3]][cnt]++;
    for(int i=1,x,y,z;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        a[0].w[num[x][3]][num[y][4-z]]++;
    }
    int lim;
    for(lim=1;lim<=61;lim++)
    {
        a[lim]=a[lim-1]*a[lim-1];
        if(cal(a[lim])>K)break;
    }
    for(int i=1;i<=cnt;i++)b.w[i][i]=1;
    for(;lim>=0;lim--)
    {
        tmp=b*a[lim];
        if(cal(tmp)<K)
            b=tmp,ans+=1ll<<lim;
    }
    if(ans>(1ll<<61))puts("-1");
    else printf("%lld\n",ans);
    return 0;
}

bzoj 4385

最近好zz呀,妈蛋这个单调队列竟然没想出来。。。。啊,怎么办啊怎么办。。。
单调队列,枚举区间的右端点,然后左端点单调,删除区间也单调,用单调队列维护删除区间就行了。。。

#include <bits/stdc++.h>
using namespace std;
#define ll long long 
#define N 2100000
int n,d;
ll p,a[N],sum[N];
int q[N],h,r,ans;
ll cal(int x){return sum[x]-sum[x-d];}
char getc()
{
    static const int LEN = 4096;
    static char buf[LEN],*S=buf,*T=buf;
    if(S == T)
    {
        T = (S=buf)+fread(buf,1,LEN,stdin);
        if(S == T)return EOF;
    }
    return *S++;
}
int read()
{
    static char ch;
    static int D;
    while(!isdigit(ch=getc()));
    for(D=ch-'0'; isdigit(ch=getc());)
        D=(D<<3)+(D<<1)+(ch-'0');
    return D;
}
int main()
{
    scanf("%d%lld%d",&n,&p,&d);
    for(int i=1;i<=n;i++)
        a[i]=read(),sum[i]=sum[i-1]+a[i];
    h=1;r=0;
    for(int i=d,j=1;i<=n;i++)
    {
        while(h<=r&&cal(q[r])<=cal(i))r--;          
        q[++r]=i;
        while(sum[i]-sum[j-1]-cal(q[h])>p)
        {
            j++;
            if(h<=r&&q[h]-d+1<j)h++;
        }
        ans=max(ans,i-j+1);
    }
    printf("%d\n",ans);
    return 0;
}

bzoj 4384

树状数组。
对于只有一个数的情况扫一遍就行了。
将一个位置表示为三个前缀和两两做差的三个数。如果 ij 满足 ij 的三个数彼此不同,那么可以用 abs(ij) 更新答案。
第一维排序解决,按第二维存两个树状数组分别是正序和倒序,然后查询时在两个里分别查第二维大的和小的。对于第三维维护位置最大值,次大值,位置取最大值时第三维的值,最小值,次小值,位置取最小值时第三维的值。
注意次大值,次小值第三维的值必须和最大值,最小值的第三维值不同。

#include <bits/stdc++.h>
using namespace std;
#define N 1100000
int n,lim,ans;
char s[N];
struct node 
{
    int a,b,c,pos;
    node(){}
    node(int a,int b,int c,int pos):a(a),b(b),c(c),pos(pos){}
    friend bool operator < (const node &r1,const node &r2)
    {return r1.a<r2.a;};
}p[N];
struct diw_tree
{
    int mx[N<<1],sx[N<<1],cx[N<<1],mn[N<<1],sn[N<<1],cn[N<<1];
    void init()
    {
        memset(mx,-0x3f,sizeof(mx));
        memset(sx,-0x3f,sizeof(sx));
        memset(mn,0x3f,sizeof(mn));
        memset(sn,0x3f,sizeof(sn));
    }
    void insert(int x,int y,int v)
    {
        for(int i=x;i<=lim;i+=i&-i)
        {
            if(v>mx[i])
            {
                if(cx[i]!=y)sx[i]=mx[i];
                mx[i]=v;cx[i]=y;
            }
            else if(y!=cx[i])
                sx[i]=max(sx[i],v);
            if(v<mn[i])
            {
                if(cn[i]!=y)sn[i]=mn[i];
                mn[i]=v;cn[i]=y;
            }
            else if(y!=cn[i])
                sn[i]=min(sn[i],v);
        }
    }
    void query(int x,int y,int v)
    {
        for(int i=x;i;i-=i&-i)
        {
            if(cx[i]!=y)ans=max(ans,mx[i]-v);
            else ans=max(ans,sx[i]-v);
            if(cn[i]!=y)ans=max(ans,v-mn[i]);
            else ans=max(ans,v-sn[i]);
        }
    }
}tr1,tr2;
int main()
{
    scanf("%d",&n);lim=(n+1)<<1;
    scanf("%s",s+1);
    for(int i=1,j;i<=n;i++)
    {
        j=i;
        while(s[j]==s[i]&&j<=n)j++;j--;
        ans=max(ans,j-i+1);i=j;
    }
    for(int i=1,a=0,b=0,c=0;i<=n;i++)
    {
        if(s[i]=='B')a++;
        if(s[i]=='C')b++;
        if(s[i]=='S')c++;
        p[i]=node(a-b,b-c,c-a,i);
    }
    tr1.init();tr2.init();
    sort(p,p+1+n);
    for(int i=0,j;i<=n;i++)
    {
        j=i;
        while(p[j].a==p[i].a&&j<=n)j++;j--;
        for(int k=i;k<=j;k++)
        {
            tr1.query(p[k].b-1+n+1,p[k].c,p[k].pos);
            tr2.query(n-(p[k].b+1)+1,p[k].c,p[k].pos);
        }
        for(int k=i;k<=j;k++)
        {
            tr1.insert(p[k].b+n+1,p[k].c,p[k].pos);
            tr2.insert(n-p[k].b+1,p[k].c,p[k].pos);
        }
        i=j;
    }
    printf("%d\n",ans);
    return 0;
}   

bzoj 4382

要是一个颜色最多有两个怎么做?
对于出现两次的颜色,在第一次出现时+1,最后一次出现时-1。然后两个点能成为分割点当且仅当两个点的前缀和相同。然后把前缀和相同的点放在一起,跑一个单调队列,就可以求出第二问了。
然后如果一个颜色出现多次的话为了防止重复hash一下每个点加的值就行了。
因为用了map跑的巨慢。。

#include <bits/stdc++.h>
using namespace std;
#define seed 233333
#define N 1100000
#define ll long long
#define ull unsigned long long 
int n,K,ans,tot,top;
ll cnt; 
ull pw[N],val[N],sum[N];
int pre[N],a[N],st[N],nex[N],vis[N];
map<ull,int>ma;
int cal(int x,int y)
{
    int t=y-x;
    return abs(t-(n-t));
}
int main()
{
    //freopen("tt.in","r",stdin);
    scanf("%d%d",&n,&K);
    pw[0]=1;
    for(int i=1;i<=n;i++)pw[i]=pw[i-1]*seed;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if(pre[a[i]])
        {   
            val[pre[a[i]]]+=pw[++tot];
            val[i]-=pw[tot];
        }
        pre[a[i]]=i;
    }
    for(int i=1;i<=n;i++)
        sum[i]=sum[i-1]+val[i];
    for(int i=1;i<=n;i++)
    {
        if(ma.count(sum[i]))
            nex[ma[sum[i]]]=i;
        ma[sum[i]]=i;
    }
    ans=n;
    for(int now=1;now<=n;now++)
        if(!vis[now])
        {
            top=0;
            for(int i=now;i;i=nex[i])
                vis[i]=1,st[++top]=i;
            cnt+=(ll)top*(top-1)/2;
            for(int i=1,j=1;i<=top;i++)
            {
                while(j<i&&cal(st[j],st[i])>cal(st[j+1],st[i]))j++;
                ans=min(ans,cal(st[j],st[i]));
            }
        }
    printf("%lld %d\n",cnt,ans);
    return 0;
}   

bzoj 4381

维护 p[i][j] 表示点i向根走j步走到的点, sum[i][j] 表示点i每次向根走j步经过的所有点的权值和。这两个第二维处理到 n 。再维护一个树剖。
然后求的时候如果一步大小小于 n ,那么直接用 sum 求一个区间和就行了。否则在树剖上一步一步走。
总时间复杂度 O(nn)

#include <bits/stdc++.h>
using namespace std;
#define N 51000
#define ll long long 
int n,tot,cnt,lim;
int a[N],b[N],head[N],nex[N<<1],to[N<<1];
int deep[N],fa[N],size[N],pos[N],bel[N],top[N],son[N];
int p[N][241],sum[N][241];
void add(int x,int y)
{
    tot++;
    nex[tot]=head[x];head[x]=tot;
    to[tot]=y;
}
void dfs1(int x,int y)
{
    fa[x]=y;size[x]=1;
    deep[x]=deep[y]+1;p[x][1]=y;
    for(int i=2;i<=lim;i++)p[x][i]=fa[p[x][i-1]];
    for(int i=1;i<=lim;i++)sum[x][i]=sum[p[x][i]][i]+a[x];

    for(int i=head[x];i;i=nex[i])
        if(to[i]!=y)
        {   
            dfs1(to[i],x),size[x]+=size[to[i]];
            son[x]=size[to[i]]>size[son[x]] ? to[i]:son[x];
        }
}
void dfs2(int x,int y,int tp)
{
    bel[pos[x]=++cnt]=x;top[x]=tp;
    if(son[x])dfs2(son[x],x,tp);
    for(int i=head[x];i;i=nex[i])
        if(to[i]!=y&&to[i]!=son[x])
            dfs2(to[i],x,to[i]);
}
int lca(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(deep[top[x]]<deep[top[y]])swap(x,y);
        x=fa[top[x]];
    }
    return deep[x]<deep[y] ? x:y;
}
int up(int x,int y)
{
    while(deep[x]-deep[top[x]]<y)
    {
        y-=deep[x]-deep[top[x]]+1;
        x=fa[top[x]];
    }
    return bel[pos[x]-y];
}
int get(int x,int y,int v)
{
    if(v<lim)return sum[x][v]-sum[y][v]+a[y];
    int ret=0,rem=0,t;
    while(top[x]!=top[y])
    {
        for(t=pos[x]-rem;t>=pos[top[x]];t-=v)
            ret+=a[bel[t]];
        t+=v;rem=v-(t-pos[top[x]]+1);
        x=fa[top[x]];
    }
    for(t=pos[x]-rem;t>=pos[y];t-=v)
        ret+=a[bel[t]];
    return ret;
}
int cal(int x,int y,int v)
{
    int z=lca(x,y),ret=0;
    int t=deep[x]-deep[z]-(deep[x]-deep[z])%v;
    t=up(x,t);
    ret+=get(x,t,v);
    if(y==z)return t==y ? ret:ret+a[y];
    int t1=v-(deep[t]-deep[z]),t2=deep[y]-deep[z]-t1;
    if(t2<0)return ret+a[y];
    int t3=up(y,t2%v),t4=up(y,t2);
    ret+=get(t3,t4,v);
    return t2%v ? ret+a[y]:ret;
}
int main()
{
    scanf("%d",&n);
    lim=sqrt(n)+1;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1,x,y;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    dfs1(1,0);dfs2(1,0,1);
    for(int i=1;i<=n;i++)scanf("%d",&b[i]);
    for(int i=1,x;i<n;i++)
    {
        scanf("%d",&x);
        printf("%d\n",cal(b[i],b[i+1],x));
    }
    return 0;
}   

bzoj 4380

最终每一个点的值一定为某个ci,将ci离散化。
区间dp,设f[i][j][k]表示当区间 [ i , j ] 中的最小值为k时的最大收益。

转移:f[i][j][k]=max(f[i][x-1][q1]+f[x+1][j][q2]+cnt*k)(q1,q2>=k)
val为左端点在 [i,x] 且右端点在 [x,i] 且值大于等于k的区间个数。

时间复杂度O(n^3*m)。

#include <bits/stdc++.h>
using namespace std;
#define N 52
#define M 4010
#define ll long long
int n,m,cnt,top;
int l[M],r[M],c[M],a[M],pos[N][N][M],val[N][N][M];
ll f[N][N][M],sum[N][N][M],st[M];
void print(int l,int r,int v)
{
    if(l>r)return;
    int t=val[l][r][v];
    print(l,pos[l][r][v]-1,t);
    printf("%d ",a[t]);
    print(pos[l][r][v]+1,r,t);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&l[i],&r[i],&c[i]),a[i]=c[i];
    cnt=m;sort(a+1,a+1+cnt);
    cnt=unique(a+1,a+1+cnt)-a-1;
    for(int i=0;i<n;i++)
        for(int j=1;j+i<=n;j++)
        {
            for(int k=j;k<=j+i;k++)
            {
                top=0;
                for(int t=1;t<=m;t++)
                    if(l[t]>=j&&r[t]<=j+i&&l[t]<=k&&r[t]>=k)
                        st[++top]=c[t];
                sort(st+1,st+1+top);
                for(int t=1,now=1;t<=cnt;t++)
                {
                    while(now<=top&&st[now]<a[t])now++;
                    ll t1=sum[j][k-1][t]+sum[k+1][j+i][t]+(ll)a[t]*(top-now+1);
                    if(t1>=f[j][j+i][t])
                    {
                        f[j][j+i][t]=t1;
                        pos[j][j+i][t]=k;
                    }
                }
            }
            for(int k=cnt;k>=1;k--)
            {
                val[j][j+i][k]=k;
                if(f[j][j+i][k]<sum[j][j+i][k+1])
                {
                    pos[j][j+i][k]=pos[j][j+i][k+1];
                    val[j][j+i][k]=val[j][j+i][k+1];
                }
                sum[j][j+i][k]=max(sum[j][j+i][k+1],f[j][j+i][k]);
            }
        }
    printf("%lld\n",sum[1][n][1]);
    print(1,n,1);
    return 0;
}

bzoj 4379

考虑假如已经知道删哪条边如何求加一条边后最大和最小的直径。
删完后图被分成两块,设两块的直径分别为 l1,l2
那么最大直径是 l1+l2+1
最小直径是 max(l1,l2,l1+12+l2+12+1)
然后按深搜顺序遍历这个树,求删掉父亲边的结果。
删边后子树那一块的直径dp算一下就行了。
剩下那块的直径求起来有点麻烦,看代码吧。。。。

#include <bits/stdc++.h>
using namespace std;
#define N 510000
int n,tot;
int head[N],nex[N<<1],to[N<<1];
int px[N],py[N],f[N],p[N],val[N],deep[N],mid[N];
int fa[N][21];
struct node
{
    int x1,y1,x2,y2,v;
    void print()
    {printf("%d %d %d %d %d\n",v,x1,y1,x2,y2);}
}a1,a2;
struct poi
{
    int x,y,v,pos;
    poi(){} 
    poi(int x,int y,int v,int pos):x(x),y(y),v(v),pos(pos){}
    friend bool operator < (const poi &r1,const poi &r2)
    {return r1.v<r2.v;};
    friend bool operator > (const poi &r1,const poi &r2)
    {return r1.v>r2.v;};
    friend poi operator + (const poi &r1,const poi &r2)
    {return poi(r1.x,r2.x,r1.v+r2.v,0);};
}pre[N];
int cmp(poi x,poi y){return x>y;}
vector<poi>v1[N],v2[N];
char getc()
{
    static const int LEN = 4096;
    static char buf[LEN],*S=buf,*T=buf;
    if(S == T)
    {
        T = (S=buf)+fread(buf,1,LEN,stdin);
        if(S == T)return EOF;
    }
    return *S++;
}
int read()
{
    static char ch;
    static int D;
    while(!isdigit(ch=getc()));
    for(D=ch-'0'; isdigit(ch=getc());)
        D=(D<<3)+(D<<1)+(ch-'0');
    return D;
}
void add(int x,int y)
{
    tot++;
    nex[tot]=head[x];head[x]=tot;
    to[tot]=y;
}   
int up(int x,int y)
{
    for(int i=0;i<=20;i++)
        if(y>>i&1)x=fa[x][i];
    return x;
}
void dfs(int x,int y)
{
    p[x]=x;deep[x]=deep[y]+1;
    px[x]=x;py[x]=x;
    fa[x][0]=y;
    for(int i=1;i<=20;i++)
        fa[x][i]=fa[fa[x][i-1]][i-1];

    for(int i=head[x];i;i=nex[i])
        if(to[i]!=y)
        {
            dfs(to[i],x);
            if(val[to[i]]>val[x])
            {
                val[x]=val[to[i]];
                px[x]=px[to[i]];py[x]=py[to[i]];
            }
            if(f[x]+f[to[i]]+1>val[x])
            {
                val[x]=f[x]+f[to[i]]+1;
                px[x]=p[x];py[x]=p[to[i]];
            }
            if(f[to[i]]+1>f[x])
            {
                f[x]=f[to[i]]+1;
                p[x]=p[to[i]];
            }
        }
    if(deep[px[x]]<deep[py[x]])
        swap(px[x],py[x]);
    mid[x]=up(px[x],val[x]/2);
}   
void dfs1(int x,int y,poi p1)
{
    if(y)
    {
        int t=val[x]+p1.v+1;
        if(t>a2.v)a2=(node){x,y,p1.x,px[x],t};
        t=max(val[x],p1.v);
        t=max(t,(val[x]+1)/2+(p1.v+1)/2+1);
        if(t<a1.v)a1=(node){x,y,mid[x],mid[y],t};   
    }
    int sum=0;
    for(int i=head[x],u;i;i=nex[i])
        if((u=to[i])!=y)
        {
            v1[x].push_back(poi(px[u],py[u],val[u],u));
            v2[x].push_back(poi(p[u],0,f[u]+1,u));
            sum++;
        }
    sort(v1[x].begin(),v1[x].end(),cmp);
    sort(v2[x].begin(),v2[x].end(),cmp);
    for(int i=head[x],u;i;i=nex[i])
        if((u=to[i])!=y)
        {
            poi t=p1;

            pre[u]=pre[x];
            if(pre[x].v>t.v)t=poi(pre[x].x,x,pre[x].v,0);
            if(v2[x][0].pos!=u)
                pre[u]=max(pre[u],v2[x][0]),t=max(t,v2[x][0]+pre[x]);
            else if(sum>1)
                pre[u]=max(pre[u],v2[x][1]),t=max(t,v2[x][1]+pre[x]);
            pre[u].v++;

            if(v1[x][0].pos!=u)t=max(t,v1[x][0]);
            else if(sum>1)t=max(t,v1[x][1]);
            if(sum>=2)
            {
                if(v2[x][0].pos==u)
                {   
                    if(sum>2)t=max(t,v2[x][1]+v2[x][2]);
                }
                else if(v2[x][1].pos==u)
                {
                    if(sum>2)t=max(t,v2[x][0]+v2[x][2]);
                }
                else t=max(t,v2[x][0]+v2[x][1]);
            }
            if(deep[t.x]<deep[t.y])swap(t.x,t.y);
            mid[x]=up(t.x,t.v/2);
            dfs1(u,x,t);
        }
}
int main()
{
    //freopen("tt.in","r",stdin);
    n=read();
    for(int i=1,x,y;i<n;i++)
    {
        x=read();y=read();
        add(x,y);add(y,x);
    }
    dfs(1,0);
    a1.v=n+1;val[1]=0;px[1]=py[1]=1;
    mid[1]=1;
    dfs1(1,0,poi(1,1,0,0));
    a1.print();a2.print();
    return 0;
}

bzoj 4378

设序列中 s 的数的个数为 x
如果x大于等于 c ,那么肯定可行。
否则如果序列中<s 的数的和 (cx)s 那么也可行。因为此时小于 s 的数的个数一定>cx 。因此每次选其中最大的 cx 个和 s x 个数,一定可以取s 次。
因此离散化一下,然后用树状数组维护一下区间个数,区间和就可以了。

#include <bits/stdc++.h>
using namespace std;
#define N 1100000
#define ll long long
#define PA pair<ll,int>
int n,m;
ll sum[N];
int cnt[N],a[N],st[N],top;
char s[11];
struct node
{
    int x,y,type;
    node(){}
    node(int x,int y,int type):x(x),y(y),type(type){}
}p[N];
void insert(int x,int v)
{
    for(int i=x;i<=top;i+=i&-i)
    {
        sum[i]+=(ll)st[x]*v;
        cnt[i]+=v;
    }
}
PA query(int x)
{
    PA ret(0,0);
    for(int i=x;i;i-=i&-i)
    {
        ret.first+=sum[i];
        ret.second+=cnt[i];
    }
    return ret;
}
int main()
{
    //freopen("tt.in","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s%d%d",s,&p[i].x,&p[i].y);
        p[i].type=(s[0]=='U');
        if(s[0]=='U')st[++top]=p[i].y;
    }
    for(int i=1;i<=n;i++)a[i]=1;
    st[++top]=0;
    sort(st+1,st+1+top);
    top=unique(st+1,st+1+top)-st-1;
    insert(1,n);
    for(int i=1;i<=m;i++)
    {
        if(p[i].type)
        {
            p[i].y=lower_bound(st+1,st+1+top,p[i].y)-st;
            insert(a[p[i].x],-1);a[p[i].x]=p[i].y;
            insert(p[i].y,1);   
        }
        else
        {
            int t=p[i].y;
            p[i].y=lower_bound(st+1,st+1+top,p[i].y)-st;
            PA t1=query(top),t2=query(p[i].y-1);
            int num=t1.second-t2.second;
            if(num>=p[i].x)puts("TAK");
            else if(t2.first>=(ll)(p[i].x-num)*t)puts("TAK");
            else puts("NIE");
        }
    }
    return 0;
}


bzoj 4377

由于n和a互质,因此 [0,n1] 的每个数恰好出现一次。
设短串的第1个位置在长串中匹配位置为 i1 x=ai1+b
如果短串的第i个数为0那么 (x+ai)%n 不在区间 [p,n1] 中。
否则 (x+ai)%n 不在区间 [0,p1] 中。
并且 i1 不在区间 [nm+1,n1] 中。既 xa(nm+1)b....(n1)b
然后求一下这些区间并的补集就是答案。

#include <bits/stdc++.h>
using namespace std;
#define N 1100000
int n,a,b,p,m,cnt,ans;
char s[N];
struct node
{
    int l,r;
    node(){}
    node(int l,int r):l(l),r(r){}
    friend bool operator < (const node &r1,const node &r2)
    {return r1.l<r2.l;};
}d[N<<2];
void insert(int l,int r)
{
    if(l<=r)d[++cnt]=node(l,r);
    else d[++cnt]=node(l,n-1),d[++cnt]=node(0,r);
}
int main()
{
    scanf("%d%d%d%d%d",&n,&a,&b,&p,&m);
    scanf("%s",s);
    for(int i=0,now=0;i<m;i++,now=(now+a)%n)
    {
        if(s[i]=='0')insert((p-now+n)%n,(n-1-now+n)%n);
        if(s[i]=='1')insert((n-now)%n,(p-1-now+n)%n);
    }   
    for(int i=1,c=(b-a+n)%n;i<m;i++,c=(c-a+n)%n)
        insert(c,c);
    sort(d+1,d+1+cnt);int en=-1;
    for(int i=1;i<=cnt;i++)
    {
        if(d[i].l>en)ans+=d[i].l-en-1,en=d[i].r;
        else en=max(en,d[i].r);     
    }
    printf("%d\n",ans+n-1-en);
    return 0;
}

bzoj 3750

记录印章中有墨水的点,按顺序枚举纸上有墨水的点,然后模拟印一下就行了。第一次竟然用了set。。。

#include <bits/stdc++.h>
using namespace std;
#define N 1100
int T;
int n,m,a,b,cnt;
bool used[N][N];
struct node
{
    int x,y;
    node(){}
    node(int x,int y):x(x),y(y){}
}p[N*N];
char s1[N][N],s2[N][N];
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d%d",&n,&m,&a,&b);
        for(int i=1;i<=n;i++)
            scanf("%s",s1[i]+1);
        cnt=0;
        for(int i=1;i<=a;i++)
        {
            scanf("%s",s2[i]+1);
            for(int j=1;j<=b;j++)
                if(s2[i][j]=='x')p[++cnt]=node(i,j);
        }
        int flag=0;
        for(int i=1;i<=n;i++)
        {   
            for(int j=1;j<=m;j++)
            {
                if(s1[i][j]=='x')
                {
                    for(int k=1;k<=cnt;k++)
                    {
                        node t1=node(i+p[k].x-p[1].x,j+p[k].y-p[1].y);
                        if(t1.x>n||t1.y>m){flag=1;break;}
                        if(s1[t1.x][t1.y]!='x'){flag=1;break;}
                        s1[t1.x][t1.y]='.';
                    }
                }
                if(flag)break;
            }
            if(flag)break;
        }
        puts(flag ? "NIE":"TAK");
    }
    return 0;
}

bzoj 3749

首先一个人是否满意和他左右以及他的取法有关。
因此可以用一个0到7的整数状压这个状态。对于第i个人0表示取第i个食物,1表示取第i+1个食物。
然后枚举第一个人的状态,设 f[i][j] 表示到第i个人取状态j是否可行。然后枚举时考虑和上一个人的状态是否匹配。

#include <bits/stdc++.h>
using namespace std;
#define N 1100000
int n;
bool v[N][8],f[N][8];
int a[N],st[N],trs[8][11],cnt[8];
short pre[N][8];
int get(int x)
{
    if(x==n+1)return 1;
    if(x==0)return n;
    return x;
}
char getc()
{
    static const int LEN = 4096;
    static char buf[LEN],*S=buf,*T=buf;
    if(S == T)
    {
        T = (S=buf)+fread(buf,1,LEN,stdin);
        if(S == T)return EOF;
    }
    return *S++;
}
int read()
{
    static char ch;
    static int D;
    while(!isdigit(ch=getc()));
    for(D=ch-'0'; isdigit(ch=getc());)
        D=(D<<3)+(D<<1)+(ch-'0');
    return D;
}
void print(int x)
{
    if(!x)return;
    print(x/10);
    putchar(x%10+'0');
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)a[i]=read();
    a[0]=a[n];a[n+1]=a[1];
    for(int i=1;i<=n;i++)
        for(int j=0;j<8;j++)
        {
            int x=(j&4) ? a[i]:a[i]<<1;
            int y=(j&1) ? a[i+1]<<1:a[i+1];
            if(j&2)v[i][j]=(y>=x);
            else v[i][j]=(y<=x);
        }
    int flag=0;
    for(int i=0;i<8;i++)
        for(int j=0;j<8;j++)
            if((i>>1)==(j&3))trs[i][++cnt[i]]=j;

    for(int now=0;now<8;now++)
        if(v[1][now])
        {
            memset(f,0,sizeof(f));
            f[1][now]=1;
            for(int i=2;i<=n;i++)
                for(int j=0,t;j<8;j++)
                    if(v[i][j])
                        for(int k=1;k<=cnt[j];k++)
                            if(f[i-1][t=trs[j][k]])
                                f[i][j]=1,pre[i][j]=t;
            for(int i=0;i<8;i++)
                if(f[n][i]&&(now>>1)==(i&3))    
                {
                    for(int j=n,t=i;j>=1;j--)
                    {
                        st[j]=t&2 ? get(j+1):get(j);
                        t=pre[j][t];
                    }
                    for(int j=1;j<=n;j++)
                        print(st[j]),putchar(' ');
                    flag=1;break;
                }
            if(flag)break;
        }
    if(!flag)puts("NIE");
    return 0;
}

bzoj 3748

这个东西1000以内打表,大于1000的时候打表可知超重数的规律为先出现一个数,然后加上一个二次函数(差分后是一个每次加2的一次函数)得到下一个数。然后依次加上16,4,12,…1,1,3,1,出现下一个数,然后再加上那个二次函数,以此类推。
因为每次加上二次函数,因此这个东西是三次的。 1018 以内依次枚举就可以。前面0ms的应该是求出了通项,Orz

#include <bits/stdc++.h>
using namespace std;
#define ll long long 
const int pre=817;
ll v3=14,v1=70,v2=29;
int f[1100];
int a[35]={16,4,12,4,16,4,5,7,12,1,3,1,10,1,1,3,1,3,1,1,3,1,3,3,1,3,1,1,3,1,};
ll ans,n;
void init()
{
    for(int i=1;i<=1000;i++)
    {
        f[i]=1000;
        for(int j=1;j*j<=i;j++)
            if(f[i-j*j]<j){f[i]=j;break;}
    }
    for(int i=1;i<=min(n,(ll)pre);i++)
    {
        int flag=0;
        for(int j=1;j<=50;j++)
            if(f[i+j]<f[i])flag=1;
        ans+=flag;
    }
}
int main()
{
    scanf("%lld",&n);
    init();
    if(n<=pre)
    {
        if(f[n]==1000)printf("-");
        else printf("%d",f[n]);
        printf(" %lld\n",ans);return 0;
    }
    for(ll i=pre;i<=n;)
    {
        if(i+v1>n)
        {
            if(n-i<=2)v3--;
            printf("%lld %lld\n",v3,ans);return 0;
        }
        i+=v1;
        if(i+126>n)
        {
            ans++;
            for(int j=0;j<30;j++)
            {
                if(i+a[j]>n)break;
                i+=a[j];ans++;
            }
            if(i==n)v3++;
            printf("%lld %lld\n",v3,ans);
            break;
        }
        v1+=v2;v2+=2;v3++;
        i+=126;ans+=31;
    }
    return 0;
}

bzoj 3747

我草,这题为什么这么多细节。。。。。
感觉写这题的心情堪比当年插头dp。。。。
顺便%一下鏼爷,虽然并不知道他写了什么。。。
算了不扯淡了。。

首先p=0,1时直接特判。
p=2时只有两种情况,模拟一下。
然后p=3,考虑从1到n往环里插点。那么插第i个点只与第i-1,i-2,i-3个点的位置有关。因为这个点不能和其他点相邻。并且一定是原来两个相邻,然后插到中间。
注意每次转移会踢掉一个点。对于那个不能在右边的条件,只需要考虑踢掉的这个点的左右都是谁就行了。
然后对于插到1和n之间的这种情况,只需要记录一个01状态表示i-1,i-2,i-3中两侧的点是否处于1,n就可以了。因为如果不处于1,n那么永远不会转移到这种情况。
f[i][j][k][t] 表示现在处理到点i,三个点位置关系为j(共6种,我用的四进制标号),相邻情况为k(两对4种),两侧的点是否处于1,n。
然后最后答案要除n。

#include <bits/stdc++.h>
using namespace std;
#define N 1100000
#define ll long long
#define mod 1000000007
#define g(x,y) ((x)>>2*(y)&3)
int n,m,p1,ans;
int a[7]={6,9,18,24,33,36,};
int b[7][3]={{0,1,2},{0,2,1},{1,0,2},{1,2,0},{2,0,1},{2,1,0}};
int trs[6][4]={{4,1,4,0},{5,3,5,2},{1,1,4,0},{1,0,4,0},{3,3,5,2},{3,2,5,2}};
int trs1[6][4][4],pos[N];
int f[2][6][4][2],p[3];
bool v[N][7];
int qpow(int x,int y)
{
    int ret=1;
    while(y)
    {
        if(y&1)ret=(ll)ret*x%mod;
        x=(ll)x*x%mod;y>>=1;
    }
    return ret;
}
int check()
{
    pos[n+1]=pos[1];
    for(int i=1;i<=n;i++)
        if(v[pos[i]][pos[i]-pos[i+1]+3])
            return 0;
    return 1;
}
void solve2()
{
    pos[1]=n;
    int i,j,k;
    for(i=n,j=2,k=1;i>j;i--,j++,k++)
        pos[i]=n-k*2+1,pos[j]=n-k*2;
    i++;j--;
    if(i-j==2)pos[i-1]=1;
    ans+=check();

    for(i=n,j=2,k=1;i>j;i--,j++,k++)
        pos[i]=n-k*2,pos[j]=n-k*2+1;
    i++;j--;
    if(i-j==2)pos[i-1]=1;
    ans+=check();
    printf("%d\n",ans);
}
void solve3()
{
    for(int i=0,t;i<6;i++)
        f[1][i][3][1]=1;

    for(int i=4;i<=n;i++)
    {
        memset(f[i&1],0,sizeof(f[i&1]));
        for(int j=0;j<6;j++)
        {   
            p[0]=b[j][0];p[1]=b[j][1];p[2]=b[j][2];
            for(int k=1;k<4;k++)
                for(int t=0;t<2;t++)
                    if(k>>t&1)
                    {
                        int l=0,r=0;
                        if(t)
                        {
                            if(!p[2])l=i;
                            if(!p[1])r=i;
                            if(k&1)
                            {   
                                if(!p[0])r=p[1]+i-3;
                                if(!p[1])l=p[0]+i-3;
                            }
                        }
                        else
                        {   
                            if(!p[0])r=i;
                            if(!p[1])l=i;
                            if(k&2)
                            {
                                if(!p[1])r=p[2]+i-3;
                                if(!p[2])l=p[1]+i-3;
                            }
                        }
                        if(r&&v[i-3][i-r])continue;
                        if(l&&v[l][l-i+6])continue;

                        (f[i&1][trs[j][t]][trs1[j][k][t]][0]+=f[~i&1][j][k][0])%=mod;

                        if(!p[0]){l=p[2]+i-3;if(v[l][l-i+6])continue;}
                        if(!p[2]){r=p[0]+i-3;if(v[i-3][i-r])continue;}

                        if(p[0]&&p[2])(f[i&1][trs[j][t]][trs1[j][k][t]][1]+=f[~i&1][j][k][1])%=mod;
                        else (f[i&1][trs[j][t]][trs1[j][k][t]][0]+=f[~i&1][j][k][1])%=mod;
                    }
            for(int k=0;k<4;k++) 
            {
                int l=0,r=0;
                if(k&1)
                {   
                    if(!p[0])r=p[1]+i-3;
                    if(!p[1])l=p[0]+i-3;
                }
                if(k&2)
                {
                    if(!p[1])r=p[2]+i-3;
                    if(!p[2])l=p[1]+i-3;
                }
                if(!p[0])l=i;
                if(!p[2])r=i;
                if(r&&v[i-3][i-r])continue;
                if(l&&v[l][l-i+6])continue; 

                if(p[2])(f[i&1][trs[j][2]][trs1[j][k][2]][1]+=f[~i&1][j][k][1])%=mod;
                else (f[i&1][trs[j][2]][trs1[j][k][2]][0]+=f[~i&1][j][k][1])%=mod;
                if(p[0])(f[i&1][trs[j][3]][trs1[j][k][3]][1]+=f[~i&1][j][k][1])%=mod;
                else (f[i&1][trs[j][3]][trs1[j][k][3]][0]+=f[~i&1][j][k][1])%=mod;
            }
        }
    }
    for(int i=0;i<6;i++)
    {   
        p[0]=n-2+b[i][0];p[1]=n-2+b[i][1];p[2]=n-2+b[i][2];
        for(int j=0;j<4;j++)
        {   
            if((j&1)&&v[p[0]][p[0]-p[1]+3])continue;
            if((j&2)&&v[p[1]][p[1]-p[2]+3])continue;
            for(int k=0;k<2;k++)
            {   
                if(k&&v[p[2]][p[2]-p[0]+3])continue;
                (ans+=f[n&1][i][j][k])%=mod;
            }
        }
    }
    ans=(ll)ans*qpow(n,mod-2)%mod;
    printf("%d\n",ans);
}
char getc()
{
    static const int LEN = 4096;
    static char buf[LEN],*S=buf,*T=buf;
    if(S == T)
    {
        T = (S=buf)+fread(buf,1,LEN,stdin);
        if(S == T)return EOF;
    }
    return *S++;
}
int read()
{
    static char ch;
    static int D;
    while(!isdigit(ch=getc()));
    for(D=ch-'0'; isdigit(ch=getc());)
        D=(D<<3)+(D<<1)+(ch-'0');
    return D;
}
int main()
{
    n=read();m=read();p1=read();
    if(n==1)return puts("1"),0;
    if(p1==0)return puts("0"),0;
    if(n==2)return puts(m ? "0":"1"),0;
    if(p1==1)return puts("0"),0;

    for(int i=1,x,y;i<=m;i++)
    {
        x=read();y=read();
        if(abs(x-y)<=3)v[x][x-y+3]=1;
    }   
    if(p1==2)
    {solve2();return 0;}

    for(int i=0;i<6;i++)
    {
        if(!b[i][1])trs1[i][3][0]=1,trs1[i][3][1]=2;
        else trs1[i][3][0]=trs1[i][3][1]=3;
        if(!b[i][2])trs1[i][1][0]=3;
        else trs1[i][1][0]=1;
        if(!b[i][0])trs1[i][2][1]=3;
        else trs1[i][2][1]=2;

        for(int j=0;j<4;j++)
        {   
            int cnt=0,t1;
            if(b[i][0]&&b[i][1]&&j&1)cnt++;
            if(b[i][1]&&b[i][2]&&j&2)cnt++;
            t1=cnt<<1;if(b[i][0])t1++;
            trs1[i][j][2]=t1;
            t1=cnt;if(b[i][2])t1=t1+2;
            trs1[i][j][3]=t1;
        }
    }
    solve3();
    return 0;
}

总结:感觉POI的题并不是按难度顺序排列的,并不知道官网上分的类有什么卵用。。。不过题还算不错(虽然脑洞)。
最后祝大家身体健康。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值