10 11

再次发现自己很菜的样子 什么都不会 还很弱。今天得分150分. T1 50分 T2 100分 T3 0分。

应该可以200+的但是没有把握好机会 很遗憾 很难受qwq. 有的时候优化的方向是一定的就不要再去思考其他的东西了。

10 11_第1张图片

10 11_第2张图片

这道题 一个 比较简单的dp 我写了好半天于是心态爆炸。关键是我没有想好如何dp 盲目转移了调半天才知道自己是...

当然除了基础状态每次的方案数也要统计不然 显然会挂掉。于是我们就可以快速得到50分的代码。

//#include
#include
#include
#include
#include
#include<string>
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include<set>
#include
#include
#include
#define INF 1000000010
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define db double
#define EPS 1e-5
#define mod 1000000007
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
    ll x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const ll MAXN=1000010;
ll n;
ll a,b,c,d,e;
ll f[MAXN][2],g[MAXN][2];
signed main()
{
    //freopen("1.in","r",stdin);
    freopen("queue.in","r",stdin);
    freopen("queue.out","w",stdout); 
    n=read();
    a=read();b=read();c=read();d=read();e=read();
    f[0][0]=d;f[0][1]=e;
    g[0][0]=1;g[0][1]=1;
    for(int i=1;i<=n;++i)
    {
        if(i-a>=0)
        {
            f[i][0]=(f[i][0]+f[i-a][0]+d*g[i-a][0])%mod;
            g[i][0]=(g[i][0]+g[i-a][0])%mod;
        }
        if(i-b>=0)
        {
            f[i][0]=(f[i][0]+f[i-b][1]+d*g[i-b][1])%mod;
            g[i][0]=(g[i][0]+g[i-b][1])%mod;
        }
        if(i-c>=0)
        {
            f[i][1]=(f[i][1]+f[i-c][1]+e*g[i-c][1])%mod;
            g[i][1]=(g[i][1]+g[i-c][1])%mod;
        }
        if(i-b>=0)
        {
            f[i][1]=(f[i][1]+f[i-b][0]+e*g[i-b][0])%mod;
            g[i][1]=(g[i][1]+g[i-b][0])%mod;
        }
    }
    //cout<
    printf("%lld\n",(f[n][0]+f[n][1])%mod);
    return 0;
}
View Code

观察这个式子我们很显然有优化就是 矩阵乘法 或者 继续考虑这道题想一下组合的做法。

于是我后悔了有式子了不优化那不是sb么 非得去想什么组合数况且自己组合数什么的也没学的怎么好GG。

直接上矩阵好了 这个矩阵还是挺好优化的。其实无非就是把这堆东西全部都放在同一行 然后列出转移矩阵即可。

关键我最愚蠢的地方是 题目都提示了矩阵乘法了a b c 都只有30大小 我都没想着搞矩阵 却因为自己想着这个矩阵可能太大了会超时什么的 真愚蠢。

关键是 我还是对矩阵不敏感 这个递推式只和后面的一项有关 多项有关那就 直接上矩阵了。以后要敏感一点。

//#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include<string>
#include
#include
#include
#include
#include
#include<set>
#include
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define INF 1000000000
#define ll long long
#define db double
#define mod 1000000007
#define pii pair
#define mk make_pair
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
    ll x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const ll MAXN=121;
ll n,m;
ll maxx,maxx1,maxx2;
ll a,b,c,d,e;
ll f[MAXN][2],g[MAXN][2];
struct wy
{
    ll w[MAXN];
    ll c[MAXN][MAXN];
    wy(){memset(w,0,sizeof(w));memset(c,0,sizeof(c));}
    friend wy operator *(wy C,wy D)
    {
        wy tmp;
        for(ll i=1;i<=m;++i)tmp.w[i]=C.w[i];
        for(ll i=1;i<=m;++i)
            for(ll j=1;j<=m;++j)
                for(ll k=1;k<=m;++k)
                    tmp.c[i][j]=(tmp.c[i][j]+C.c[i][k]*D.c[k][j])%mod;
        return tmp;
    }
    friend wy operator -(wy C,wy D)
    {
        wy tmp;
        for(ll i=1;i<=m;++i)
            for(ll j=1;j<=m;++j)
                tmp.c[i][j]=C.c[i][j];
        for(ll i=1;i<=m;++i)
            for(ll j=1;j<=m;++j)
                tmp.w[i]=(tmp.w[i]+C.w[j]*D.c[j][i])%mod;
        return tmp;
    }
    friend wy operator ^(wy A,ll p)
    {
        wy tmp=A;
        while(p)
        {
            if(p&1)tmp=tmp-A;
            p=p>>1;
            A=A*A;
        }
        return tmp;
    }
}A;
inline void get_pre(ll x)
{
    f[0][0]=d;f[0][1]=e;
    g[0][0]=1;g[0][1]=1;
    for(ll i=1;i<=x;++i)
    {
        if(i-a>=0)
        {
            f[i][0]=(f[i][0]+f[i-a][0]+d*g[i-a][0])%mod;
            g[i][0]=(g[i][0]+g[i-a][0])%mod;
        }
        if(i-b>=0)
        {
            f[i][0]=(f[i][0]+f[i-b][1]+d*g[i-b][1])%mod;
            g[i][0]=(g[i][0]+g[i-b][1])%mod;
        }
        if(i-c>=0)
        {
            f[i][1]=(f[i][1]+f[i-c][1]+e*g[i-c][1])%mod;
            g[i][1]=(g[i][1]+g[i-c][1])%mod;
        }
        if(i-b>=0)
        {
            f[i][1]=(f[i][1]+f[i-b][0]+e*g[i-b][0])%mod;
            g[i][1]=(g[i][1]+g[i-b][0])%mod;
        }
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    freopen("queue.in","r",stdin);
    freopen("queue.out","w",stdout);     
    n=read();
    a=read();b=read();c=read();d=read();e=read();
    maxx1=max(a,b);maxx2=max(b,c);
    maxx=max(maxx1,maxx2);
    get_pre(maxx);
    
    for(ll i=1;i<=maxx;++i)A.w[i]=g[i][0];
    A.c[maxx-a+1][maxx]=1;A.c[2*maxx-b+1][maxx]=1;
    for(ll j=maxx-1;j;--j)A.c[j+1][j]=1;
    
    for(ll i=maxx+1;i<=2*maxx;++i)A.w[i]=g[i-maxx][1];
    A.c[2*maxx-c+1][2*maxx]=1;A.c[maxx-b+1][2*maxx]=1;
    for(ll j=maxx*2-1;j>=maxx+1;--j)A.c[j+1][j]=1;
    
    for(ll i=maxx*2+1;i<=maxx*3;++i)A.w[i]=f[i-maxx*2][0];
    A.c[3*maxx-a+1][maxx*3]=1;A.c[maxx-a+1][maxx*3]=d;
    A.c[4*maxx-b+1][maxx*3]=1;A.c[2*maxx-b+1][maxx*3]=d;
    for(ll i=maxx*3-1;i>=maxx*2+1;--i)A.c[i+1][i]=1;
    
    for(ll i=maxx*3+1;i<=maxx*4;++i)A.w[i]=f[i-maxx*3][1];
    A.c[maxx*4-c+1][maxx*4]=1;A.c[2*maxx-c+1][maxx*4]=e;
    A.c[maxx*3-b+1][maxx*4]=1;A.c[maxx-b+1][4*maxx]=e;
    for(ll i=maxx*4-1;i>=maxx*3+1;--i)A.c[i+1][i]=1;
    
    //printf("%lld\n",A.w[maxx]);
    //printf("%lld\n",A.w[2*maxx]);
    
    m=maxx<<2;A=A^(n-maxx);
    
    //printf("%lld\n",A.w[maxx]);
    //printf("%lld\n",A.w[2*maxx]);
    
    printf("%lld\n",(A.w[maxx*3]+A.w[maxx*4])%mod);
    return 0;
}
View Code

10 11_第3张图片

10 11_第4张图片

字符串的问题 LCP 最长公共前缀 。两两匹配问题首先就是 二分图最大带权匹配了。不会KM 能跑费用流啊。

考虑100分 n^2建图都完成不了 考虑 一种更加的匹配方法 题目必然有某些特殊的性质。

考虑二分图转换匹配的问题 一个点把另一个点的对象给占了 的充要条件是 另一个点还有一个对象 且 交换后对答案更优,但是这种情况在LCP出现的时候就变得不再可能了。

证明的话考虑交换后能否得到一个更优的解,(当时想到了,但是没证明qwq 现在看起来也不好证明的样子  

画个trie 就可以证明了 把最优解拆散一定不优那么 就是每次选取全局最优解了 但是还是很不好做的样子。

上trie 然后发现LCP很好求全局最优解也很好求 其实核心就是再dfs一遍即可。

//#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include<string>
#include
#include
#include
#include
#include
#include<set>
#include
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define INF 1000000000
#define ll long long
#define db double
#define mod 1000000007
#define pii pair
#define mk make_pair
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=3000000;
int n,ans,len,tot,p,id=1;
char a[MAXN];
int t[MAXN][26],sz[MAXN],g[MAXN],d[MAXN];
int A,B;
inline void insert()
{
    p=1;
    for(int i=1;i<=len;++i)
    {
        int c=a[i]-'a';
        if(!t[p][c])t[p][c]=++id;
        d[t[p][c]]=d[p]+1;
        p=t[p][c];
    }
    ++sz[p];
}
inline void change()
{
    p=1;
    for(int i=1;i<=len;++i)
    {
        int c=a[i]-'a';
        if(!t[p][c])
        {
            ++g[p];
            return;
        }
        p=t[p][c];
    }
    ++g[p];
}
inline void dfs(int x)
{
    for(int i=0;i<=25;++i)
    {
        if(t[x][i])
        {
            dfs(t[x][i]);
            sz[x]+=A;
            g[x]+=B;
        }
    }
    int w=min(g[x],sz[x]);
    ans+=w*d[x];
    A=sz[x]-w;B=g[x]-w;
}
int main()
{
    //freopen("1.in","r",stdin);
    freopen("choose.in","r",stdin);
    freopen("choose.out","w",stdout);
    n=read();
    for(int i=1;i<=n;++i)
    {
        scanf("%s",a+1);
        len=strlen(a+1);
        insert();
    }
    for(int i=1;i<=n;++i)
    {
        scanf("%s",a+1);
        len=strlen(a+1);
        change();
    }
    dfs(1);
    //cout<
    printf("%d\n",ans);
    return 0;
}
View Code

10 11_第5张图片

10 11_第6张图片

10 11_第7张图片

我是个sb 没有任何能力 也不会思考 也不会打代码 也不会学习文化课 也不会守护。

我 还有什么用处呢?

首先是区间 修改的问题 线段树很难维护 这种区间立方的区间修改 但是修改次数还是可以维护的我们 可以维护修改次数 然后 每次单点暴力查询。

然后 发现这个幂也很大 但是我们有欧拉降幂直接降到logn 或者还有其他的方法 如倍增(发现倍增真强什么都能搞。

设 f[i][j] 表示 i这个数字经过j个三次幂后变成的数字 那么显然有 f[i][j]=f[f[i][j-1]][j-1];

发现暴力修改复杂度 更高且还没有真正的暴力高 这是因为 考虑 只要区间>=14的我们直接输出Yes 即可。

因为存在一个 命题 设 当前区间为 x 那么这个区间的取值 为 2^x设其没有重复的取值出现 其区间的值域为 [x,x*len] 如果都不相同的话 那么显然有

x*len>=2^x 我们可以断定当2^x >x*len 时必然有 重复的取值出现 因为 此时不会有两个区间值相同且有并集 如果存在那么也同时存在两个不同的集合值相同 但同时 值域已经被爆了 所以必然有重复的数字出现 。

由于上述 命题 两个区间必然不相交 故解得x 为14 ;所以大于等于14的区间都是Yes 剩下的爆搜解决 即可。 

我可真的是个sb 写个爆搜不清空数组 写个线段树区间修改都能打错。

//#include
#include
#include
#include
#include
#include<string>
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include<set>
#include
#include
#include
#define INF 1000000010
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define db double
#define EPS 1e-5
#define l(p) t[p].l
#define r(p) t[p].r
#define sum(p) t[p].sum
#define zz p<<1
#define yy p<<1|1
#define mod 1000000007
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=200010,maxn=1010;
int n,m,h,flag,v;
int Log[MAXN],a[MAXN],q[MAXN],vis[MAXN];
int f[MAXN][20];
struct wy
{
    int l,r;
    int sum;
}t[MAXN<<2];
inline void build(int p,int l,int r)
{
    l(p)=l;r(p)=r;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(zz,l,mid);
    build(yy,mid+1,r);
}
inline void pushdown(int p)
{
    sum(zz)+=sum(p);
    sum(yy)+=sum(p);
    sum(p)=0;return;
}
inline void change(int p,int l,int r)
{
    if(l<=l(p)&&r>=r(p)){++sum(p);return;}
    int mid=(l(p)+r(p))>>1;
    if(sum(p))pushdown(p); 
    if(l<=mid)change(zz,l,r);
    if(r>mid)change(yy,l,r);
}
inline int ask(int p,int x)
{
    if(l(p)==r(p)){int w=sum(p);sum(p)=0;return w;}
    int mid=(l(p)+r(p))>>1;
    if(sum(p))pushdown(p);
    if(x<=mid)return ask(zz,x);
    return ask(yy,x);
}
inline int get_x(int x,int y)
{
    for(int i=0;i<=Log[y];++i)
        if((1<f[x][i];
    return x;
}
inline void dfs(int x,int xx,int w,int p)
{
    if(flag)return;
    if(x==xx+1)
    {
        if(!p)return;
        if(!w)flag=1;
        if(w>0)q[++h]=w,vis[w]=1;
        return;
    }
    dfs(x+1,xx,w,p);
    dfs(x+1,xx,w-a[x]-1,1);
    dfs(x+1,xx,w+a[x]+1,1);
}
inline void dfs1(int x,int xx,int w,int p)
{
    if(flag)return;
    if(x==xx+1)
    {
        if(!p)return;
        if(!w)flag=1;
        if(w>0)if(vis[w])flag=1;
        return;
    }
    dfs1(x+1,xx,w,p);
    dfs1(x+1,xx,w-a[x]-1,1);
    dfs1(x+1,xx,w+a[x]+1,1);
}
inline void cle()
{
    for(int i=1;i<=h;++i)vis[q[i]]=0;
    return;
}
inline int find(int l,int r)
{
    for(int i=l;i<=r;++i)
    {
        int w=ask(1,i);
        a[i]=get_x(a[i],w);
    }
    h=0;flag=0;
    int mid=((l+r)>>1);
    dfs(l,mid,0,0);
    if(flag)return 1;
    dfs1(mid+1,r,0,0);
    if(flag)return 1;
    return 0;
}
signed main()
{
    //freopen("1.in","r",stdin);
    freopen("birthday.in","r",stdin);
    freopen("birthday.out","w",stdout);
    n=read();m=read();v=read();
    for(int i=1;i<=n;++i)a[i]=read();
    build(1,1,n);
    for(int i=2;i<=m;++i)Log[i]=Log[i>>1]+1;
    for(int i=1;i0]=i*i*i%v;
    for(int i=1;i<=Log[m];++i)
        for(int j=1;j1]][i-1];
    for(int i=1;i<=m;++i)
    {
        int op,l,r;
        op=read();l=read();r=read();
        if(op==1)
        {
            if(r-l+1>=14)puts("Yes");
            else 
            {
                cle();
                if(find(l,r))puts("Yes");
                else puts("No");
            }
        }
        else change(1,l,r);
    }
    return 0;
}
View Code

 复杂度还是很稳的 2*1e8左右

你可能感兴趣的:(10 11)