5.25 考试+修改

论改题只用两分钟的速度QAQ

其实就是换了个数组名字,加上加了一句话

第一题:

首先考虑k=1的情况,考虑构造转移矩阵A

ans*(A^0+A^1+……+A^(n-1))

然后括号里的式子等比数列求和一下

是(A^0-A^n)/(A^0-A^1)

涉及到除法,手动矩阵求逆就可以了

然后这个式子就变成了一个矩阵

我们考虑k>1的情况,发现扩维不过就是又乘了一次这个矩阵

然后把这个矩阵自乘k次即可

(考试的时候犯傻,没有想到k>1的时候直接自乘k次就可以了,下午加了一句话就A了)

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

typedef long long LL;
const int mod=1e9+7;
int T,n,k;
LL f[1000010];
LL sum;
struct Matrix{
    LL a[2][2];
    void clear(){memset(a,0,sizeof(a));}
}A,B,C,ans; 

void DFS(int num,int pos){
    if(pos>k){
        num=num-k+1;
        sum+=f[num];
        if(sum>=mod)sum-=mod;
        return;
    }
    for(int i=1;i<=n;++i)DFS(num+i,pos+1);
}
Matrix operator *(const Matrix &A,const Matrix &B){
    Matrix C;C.clear();
    for(int i=0;i<2;++i){
        for(int j=0;j<2;++j){
            for(int k=0;k<2;++k){
                C.a[i][j]=C.a[i][j]+A.a[i][k]*B.a[k][j]%mod;
                if(C.a[i][j]>=mod)C.a[i][j]-=mod;
                if(C.a[i][j]<0)C.a[i][j]+=mod;
            }
        }
    }return C;
}
Matrix pow_mod(Matrix v,int p){
    Matrix tmp;tmp.clear();
    for(int i=0;i<2;++i)tmp.a[i][i]=1;
    while(p){
        if(p&1)tmp=tmp*v;
        v=v*v;p>>=1;
    }return tmp;
} 

int main(){
    scanf("%d",&T);
    f[0]=0;f[1]=1;
    for(int i=2;i<=1000000;++i){
        f[i]=f[i-1]+f[i-2];
        if(f[i]>=mod)f[i]-=mod;
    }
    while(T--){
        scanf("%d%d",&n,&k);
        A.clear();A.a[0][1]=1;A.a[1][0]=1;A.a[1][1]=1;
        C=pow_mod(A,n);
        for(int i=0;i<2;++i){
            for(int j=0;j<2;++j){
                C.a[i][j]=(i==j)-C.a[i][j];
                if(C.a[i][j]<0)C.a[i][j]+=mod;
            }
        }
        B.clear();B.a[0][1]=-1;B.a[1][0]=-1;B.a[1][1]=-1;
        C=C*B;C=pow_mod(C,k);
        ans.clear();ans.a[0][1]=1;ans=ans*C;
        printf("%lld\n",(ans.a[0][1]%mod+mod)%mod);
    }return 0;
}

第二题显然是要树分治的

HEOI的思路,我们二分答案,把小于答案的设为-1,大于等于答案的设为1

我们很容易发现答案具有单调性,判断的话只需要判断树上是否有一条经过边数在[L,R]的长度>=0的边即可

然后就是树分治啦,注意这里如果每次用线段树查最大值

是O(nlog^3n),30s是肯定能过的

不过可以优化,我们发现每次查最大值都是查定长区间的最大值,我们可以通过BFS使得dep有序化,之后做单调队列就可以优化到O(nlog^2n)

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

const int maxn=100010;
const int oo=0x7fffffff/3;
int n,L,R;
int A,B;
int h[maxn],cnt=0;
struct edge{
    int to,next,w;
}G[maxn<<1];
struct Edge{
    int u,v,w;
}c[maxn];
bool vis[maxn];
int f[maxn],g,sum;
int w[maxn],top=0;
struct OP{
    int dep,dis;
}st[maxn];
int tim=0;
struct Seg_Tree{
    int mx,t;
}t[maxn<<2]; 

void add(int x,int y,int z){
    ++cnt;G[cnt].to=y;G[cnt].next=h[x];G[cnt].w=z;h[x]=cnt;
}
void read(int &num){
    num=0;char ch=getchar();
    while(ch<‘!‘)ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘)num=num*10+ch-‘0‘,ch=getchar();
}
void UPD(int o,int L,int R,int p,int v){
    if(L==R){
        if(t[o].t!=tim){
            t[o].t=tim;
            t[o].mx=v;
        }else t[o].mx=max(t[o].mx,v);
        return;
    }
    int mid=(L+R)>>1;
    int A=(o<<1),B=(A|1);
    if(p<=mid)UPD(A,L,mid,p,v);
    else UPD(B,mid+1,R,p,v);
    t[o].t=tim;
    A=(t[A].t==tim?t[A].mx:-oo);
    B=(t[B].t==tim?t[B].mx:-oo);
    t[o].mx=max(A,B);
}
int ask(int o,int L,int R,int x,int y){
    if(L>=x&&R<=y){
        if(t[o].t!=tim)return -oo;
        return t[o].mx;
    }
    int mid=(L+R)>>1;
    if(y<=mid)return ask(o<<1,L,mid,x,y);
    else if(x>mid)return ask(o<<1|1,mid+1,R,x,y);
    else return max(ask(o<<1,L,mid,x,y),ask(o<<1|1,mid+1,R,x,y));
}
void cmax(int &a,int b){if(b>a)a=b;}
void Get_G(int u,int fa){
    f[u]=0;w[u]=1;
    for(int i=h[u];i;i=G[i].next){
        int v=G[i].to;
        if(v==fa||vis[v])continue;
        Get_G(v,u);
        w[u]+=w[v];
        cmax(f[u],w[v]);
    }cmax(f[u],sum-w[u]);
    if(f[g]>f[u])g=u;
}
void Get_dis(int u,int f,int d,int D){
    ++top;st[top].dep=d;st[top].dis=D;
    for(int i=h[u];i;i=G[i].next){
        int v=G[i].to;
        if(vis[v]||v==f)continue;
        Get_dis(v,u,d+1,D+G[i].w);
    }return;
}
bool Get_div(int u){
    vis[u]=true;tim++;
    UPD(1,0,n,0,0);
    for(int i=h[u];i;i=G[i].next){
        int v=G[i].to;
        if(vis[v])continue;
        top=0;Get_dis(v,-1,1,G[i].w);
        for(int j=1;j<=top;++j){
            int dep=st[j].dep,dis=st[j].dis;
            int A=L-dep,B=R-dep;
            if(B<0)continue;
            if(A<0)A=0;
            int now=ask(1,0,n,A,B);
            if(now+dis>=0)return true;
        }
        for(int j=1;j<=top;++j)UPD(1,0,n,st[j].dep,st[j].dis);
    }
    for(int i=h[u];i;i=G[i].next){
        int v=G[i].to;
        if(vis[v])continue;
        g=0;sum=w[v];
        Get_G(v,-1);
        if(Get_div(g))return true;
    }return false;
}
bool check(){
    memset(vis,false,sizeof(vis));
    f[0]=oo;g=0;sum=n;
    Get_G(1,-1);
    if(Get_div(g))return true;
    return false;
} 

int main(){
    read(n);read(L);read(R);
    A=oo;B=-oo;
    for(int i=1;i<n;++i){
        read(c[i].u);read(c[i].v);read(c[i].w);
        A=min(A,c[i].w);B=max(B,c[i].w);
    }
    while(A<B){
        int mid=A+((B-A+1)>>1);
        memset(h,0,sizeof(h));cnt=0;
        for(int i=1;i<n;++i){
            if(c[i].w>=mid)add(c[i].u,c[i].v,1),add(c[i].v,c[i].u,1);
            else add(c[i].u,c[i].v,-1),add(c[i].v,c[i].u,-1);
        }
        if(check())A=mid;
        else B=mid-1;
    }
    printf("%d\n",A);
    return 0;
}

第三题这么丝薄的题目我因为end是系统关键字挂掉了!

没有人跟我说啊喂!以后再也不用英文单词了!

还是HEOI的题目的变形

我们差分之后倍长,多出来的那一半是原来差分的序列的取反情况

之后我们建出反串的SAM

对于每次询问,在后一半找到取反的序列,在前一半查询有多少的位置和他的LCP>=len

可以直接搞出parent树之后用树状数组维护DFS序+倍增找位置就可以了

注意到区间不能重叠,也就是对前一半的区间有限制,我们把树状数组换成可持久化线段树就可以啦

时间复杂度O(nlogn)

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

const int maxn=400010;
const int oo=0x7fffffff;
int n,cnt,la,m,L,R;
int a[maxn],b[maxn];
int P[maxn];
int h[maxn],c=0;
int pos[maxn],ed[maxn],tot=0;
int dep[maxn];
int anc[maxn][20];
struct edge{
    int to,next;
}G[maxn];
struct Node{
    map<int,int>nxt;
    int len,link;
}st[maxn];
int rt[maxn],sum;
struct Seg_Tree{
    int L,R,v;
}t[11000010];  

void read(int &num){
    num=0;char ch=getchar();
    while(ch<‘!‘)ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘)num=num*10+ch-‘0‘,ch=getchar();
}
void add_edge(int x,int y){
    c++;G[c].next=h[x];G[c].to=y;h[x]=c;
}
int add(int c){
    int cur=++cnt;
    st[cur].len=st[la].len+1;
    int p;
    for(p=la;p!=-1&&!st[p].nxt.count(c);p=st[p].link)st[p].nxt[c]=cur;
    if(p==-1)st[cur].link=0;
    else{
        int q=st[p].nxt[c];
        if(st[q].len==st[p].len+1)st[cur].link=q;
        else{
            int clone=++cnt;
            st[clone]=st[q];st[clone].len=st[p].len+1;
            for(;p!=-1&&st[p].nxt[c]==q;p=st[p].link)st[p].nxt[c]=clone;
            st[q].link=st[cur].link=clone;
        }
    }la=cur;return la;
}
void build_Graph(){
    for(int i=1;i<=cnt;++i)add_edge(st[i].link,i);
}
void Get_DFS(int u,int f){
    pos[u]=++tot;
    for(int i=h[u];i;i=G[i].next){
        int v=G[i].to;
        if(v==f)continue;
        dep[v]=dep[u]+1;
        Get_DFS(v,u);
    }ed[u]=tot;
}
void pre_LCA(){
    for(int i=0;i<=cnt;++i){
        anc[i][0]=st[i].link;
        for(int j=1;(1<<j)<=cnt;++j)anc[i][j]=-1;
    }
    for(int j=1;(1<<j)<=cnt;++j){
        for(int i=0;i<=cnt;++i){
            if(anc[i][j-1]!=-1){
                int a=anc[i][j-1];
                anc[i][j]=anc[a][j-1];
            }
        }
    }return;
}
void build(int &o,int L,int R){
    o=++sum;
    if(L==R)return;
    int mid=(L+R)>>1;
    build(t[o].L,L,mid);
    build(t[o].R,mid+1,R);
}
void UPD(int &o,int L,int R,int p){
    t[++sum]=t[o];o=sum;
    if(L==R){t[o].v++;return;}
    int mid=(L+R)>>1;
    if(p<=mid)UPD(t[o].L,L,mid,p);
    else UPD(t[o].R,mid+1,R,p);
    t[o].v=t[t[o].L].v+t[t[o].R].v;
}
int ask(int o,int L,int R,int x,int y){
    if(L>=x&&R<=y)return t[o].v;
    int mid=(L+R)>>1;
    if(y<=mid)return ask(t[o].L,L,mid,x,y);
    else if(x>mid)return ask(t[o].R,mid+1,R,x,y);
    else return ask(t[o].L,L,mid,x,y)+ask(t[o].R,mid+1,R,x,y);
}
int Get_pos(int u,int len){
    int log;
    for(log=0;(1<<log)<=dep[u];++log);--log;
    for(int i=log;i>=0;--i){
        if(anc[u][i]!=-1&&st[anc[u][i]].len>=len)u=anc[u][i];
    }return u;
}
/*void print(){
    for(int i=0;i<=cnt;++i){
        for(map<int,int>::iterator it=st[i].nxt.begin();it!=st[i].nxt.end();++it){
            printf("%d %d %d\n",i,(it->first),(it->second));
        }printf("\n");
    }return;
}*/

int main(){
    read(n);cnt=la=0;st[0].link=-1;
    for(int i=1;i<=n;++i)read(a[i]);
    n--;
    for(int i=1;i<=n;++i)b[i]=a[i+1]-a[i];
    b[n+1]=oo;
    for(int i=n+2;i<=(n<<1)+1;++i)b[i]=-b[i-n-1];
    n=(n<<1|1);
    for(int i=n;i>=1;--i)P[i]=add(b[i]);
    //print();
    build_Graph();Get_DFS(0,-1);pre_LCA();
    build(rt[0],1,tot);
    for(int i=1;i<=n;++i){
        rt[i]=rt[i-1];
        UPD(rt[i],1,tot,pos[P[i]]);
    }
    read(m);
    for(int i=1;i<=m;++i){
        read(L);read(R);
        if(L==R){printf("%d\n",(n>>1));continue;}
        int v=Get_pos(P[L+(n>>1)+1],R-L);
        int A,B,C;L=L-(R-L+1);
        A=ask(rt[(n>>1)],1,tot,pos[v],ed[v]);
        B=ask(rt[R],1,tot,pos[v],ed[v]);
        if(L<0)C=0;
        else C=ask(rt[L],1,tot,pos[v],ed[v]);
        B=B-C;A=A-B;
        printf("%d\n",A);
    }return 0;
}

今天考试非常的不开心

lemon和网站上同时卡了我end的关键字,第三题爆零了

lemon还是在win下测得,第二题还挂了两个点的栈空间

第一题上午也是丝薄了,忘记自乘k次了QAQ

UPD:感觉考试暴露了自己一些弱点

1、矩阵求逆不太会写

2、树分治用单调队列优化不熟练

3、对于矩阵的概念和应用不了解

今天需要做的题目:

1、51NOD 约数之和

2、BZOJ 矩阵求逆的裸题

3、WC 重建计划

4、ZJOI 细胞

一些要做但是可以暂时坑掉的题目:

1、生日聚会

2、棋盘制作

3、矩形面积并

4、基站选址

5、最小割

时间: 2024-10-28 11:23:14

5.25 考试+修改的相关文章

10.25 考试总结

10.25 考试总结 距离退役死亡越来越近. circle 其实只要暴力查找就可以了 搜索跑图,稍微注意一下不要让法师反复横跳就可以了,多加一个数据就可以解决只要一找到已经访问过的就证明是环了.... 开始想复杂了,以为可能有一个奇葩的图....原来就是一个简单的环啊 path 其实就是找必经之路,把一些可以多选择的....缩点,实际上就是tarjan缩点, 然后找桥. 桥就是我们要的必经之路 然后又要去学tarjan.....模板一下背不下来 mst 首先用prim 作出最小生成树.然后考虑修

3.25 考试

又是考得惨不忍睹的一次 最近这个状态真的是-- 1.简单的数列 不想怎么说的一道题目 无奈qwq 考场忘开longlong痛失30分可以显然发现这是个跟求逆序对那类题目有些相似的玩意儿考场想到了树状数组 然而实在是忘得差不多QAQ!注意 近期不能只往前赶了!还要时不时复习一下之前的! 1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #inclu

5.28 考试修改+总结

今天又是一个悲伤的故事,所有排名比我高的人第一题都A了 而我第一题爆零了 但是开心的事情是:第一题没有说是简单图,所以题解是错的 不管怎么样,将错就错吧 今天下午断网了,所以这时候才写blog 第一题 由于题目中没有给出欧拉图的概念,所以我完全不知道它在说啥,于是就爆零了 然后欧拉图就是存在欧拉回路的简单图,具有以下特点: 1.联通 2.度数都是偶数 显然我们将错就错看题目的话,把欧拉图数目*(n*(n-1)/2+1)就可以得到答案了 然后我们很容易知道度数均为偶数的图的数目是2^((n-1)*

6.15 考试修改+总结

昨天考崩了QAQ 几乎写全了暴力分,然而并没有什么卵用 因为只要A掉一道题就比我分高了,比我分高的也至少A掉了一道题QAQ 感觉到一丝淡淡的忧桑 貌似THUSC最后听讲课的那些人几乎都A了两题 看来我的THUSC果然只是RP好啊 第一题 显然选色数最少的颜色,设颜色数为m 考虑存在某个点的方案数,设这个点到k距离i个点 则方案数为(n-1-i)!/ ((m-i)!*j!*k!……) j,k等是其他颜色的色数 总方案也是非常好算的,这样我们就可以计算每个点对于期望的贡献了 这样做是O(n^2)的

5.26 考试修改+总结

论写5K+的代码在只有样例的条件下都可以调对 由此可见,勇气才是成功的关键 先放题解吧 第一题上午写的暴力不小心忘记题目换根之后还会染色了 然后就挂成了5分QAQ 有很大的部分分是SDOI染色,还有一部分是旅行 但是考试犯懒没有写 很容易发现任何一种颜色在树上都是连续的一段 那么我们不妨这么定义,如果一条边两端颜色不相同,我们定义为虚边,会对子树每个答案产生+1的贡献 如果两端颜色相同,我们定义为实边,不会产生贡献 不难发现,这样定义后的实边和虚边的性质和LCT的定义是一样的 我们考虑使用LCT

6.3 考试修改+总结

今天下午考试被FFT和数论题目翔了一脸QAQ 做的是Newscafe杯的题目 第一题 异化多肽 显然构造多项式f 答案是f+f^2+f^3…… 化简一下得1/(1-f) 之后多项式求逆即可 考试的时候推了好久的多项式求逆的式子(感觉自己什么都忘光了 #include<cstdio> #include<cstring> #include<iostream> #include<cstdlib> #include<algorithm> #define

6.11 考试修改+总结

第三题至今没敢写,感觉好恐怖QAQ 今天考得好糟糕 第一题只写了10分的暴力+(k=1)20分的网络流 后来题解告诉我k>1的时候可以分治到k=1,每层分治解决方法是同k=1的 考试的时候没有注意到2^k这个比较神奇的可以分治的性质 而且自己考场上丝薄了,没有发现因为是二分图可以直接跑欧拉回路的性质,而是裸套网络流模型 第二题其实已经接近想出了题解 自己考试的时候成功证明了暴力的复杂度是线性的 但是没有想到如何寻找0-1对,然后就只能暴力用Splay维护1所在的位置了 默默祈祷数据不要太卡我的做

6.10 考试修改+总结+颓废记

昨天晚上得到了非常不爽的消息,zcg要去给高一讲课,而我并不能去 虽然什么事情并不能都顺着我的心意来吧,但是这件事情真是让人越想越不痛快 要知道,我从去年就一直期待着给高一讲课呢 所以今天考试非常不开心,一般这个时候我会选择爆零的 但是想了想觉得爆零太难看,就看了看好像第一题可做 在教学楼里颓废了好久然后吃了点东西,用最后的时间码完了第一题 (反正二.三题我没看出来怎么做,所以暴力也不想写了 然后惊讶的是,只有第一题程序的窝rank1了QAQ 先放题解吧 第一题: 首先我们注意到转置的实质是某个

5.27 考试修改+总结

这是一个悲伤的故事 上午写manacher的时候往里面加#号,然后统计有效字符的个数 然后我就开始模拟,一个长度为6的串我都能数错有多少个有效字符 我把2个字符数成了3个!然后暴力就挂掉了5分.. 为什么这几天的暴力总是会挂掉,真是奇怪(看来是最近自己内心不太稳了 (大概是被那个梦吓得吧QAQ) 今天又A了两道题目,感觉天天都是两道正解+挂掉的暴力QAQ 先放题解吧 第一题是之前数位DP坑掉的题目,然后今天尝试着写了写,感觉不是很难 但是并不是用数位DP做的 先考虑加密的情况,我们只需要统计每一