20191005

前言

  • 发现昨天竟然没总结这场考试。
  • 这场考试还是有许多值得纪念的地方的,如:
  • 发现自己对约瑟夫问题理解不是很深刻
  • 读题不读平方导致柿子推不出来
  • T3打了半个正解因为数组只开了一维
  • 然后我就死了……

T1

  • 发现要深刻理解的话并不简单,于是我找到了理解很深刻的任NB对我进行讲解。
  • 然后我还是不会决定先扔一个链接就跑。
  • 跑之前再留下一个带有我的一些理解的垃圾代码吧。
#include
using namespace std;
int main(){
    int T,n,m,ans;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m),ans=0;
        //初始只有一个编号为0的人,我们从只有1人的游戏倒退回有n人的游戏
        for(register int i=2,z;i<=n;i+=z){
        //i代表轮数,相当于执行完本轮后还剩下i-1个人
        //i+=z相当于由i轮跳至i+z轮
            z=(i-ans-1)/m+1;
            if(i+z>n)z=n-i+1;//计算出跳至边界n所需最小轮数
            ans=(ans+z*m)%(i+z-1);//维护答案
        }
        printf("%d\n",ans+1);//因为编号由0开始,所以真实位置+1
    }
    return 0;
}
View Code

T2

  • 式子很简单,请自行化简。
  • 注意只要是两个形式相同的柿子相乘就可以转化成区间和的相乘。
#include
#define L tr[k].l
#define R tr[k].r
#define ll long long
using namespace std;
int const N=1e6+5,mod=20170927;
struct node{
    int l,r;
    ll xw,yw,xy;
}tr[N<<2];
int n,m;
int id[N];
ll xt,yt,xyt;
inline int read(){
    int ss(0);char bb(getchar());
    while(bb<48||bb>57)bb=getchar();
    while(bb>=48&&bb<=57)ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar();
    return ss;
}
constexpr ll pf(ll x){
    return x*x%mod;
}
inline int down(int x){
    return xmod;
}
inline void update(int k){
    int lk=k<<1,rk=lk|1;
    tr[k].xw=down(tr[lk].xw+tr[rk].xw),tr[k].yw=down(tr[lk].yw+tr[rk].yw);
    tr[k].xy=down(tr[lk].xy+tr[rk].xy);
    return ;
}
void build(int x,int y,int k){
    L=x,R=y;
    if(x==y){
        tr[k].xw=read(),tr[k].yw=read();
        tr[k].xy=tr[k].xw*tr[k].yw%mod;
        tr[k].xw=pf(tr[k].xw);
        tr[k].yw=pf(tr[k].yw);
        id[x]=k;
        return ;
    }
    int mid=x+y>>1;
    build(x,mid,k<<1),build(mid+1,y,k<<1|1);
    return update(k);
}
inline void change(int k,ll x,ll y){
    tr[k].xw=pf(x),tr[k].yw=pf(y);
    tr[k].xy=x*y%mod;
    k>>=1;
    while(k)update(k),k>>=1;
    return ;
}
void ask(int x,int y,int k){
    if(L>=x&&R<=y){xt+=tr[k].xw,yt+=tr[k].yw;xyt+=tr[k].xy;return ;}
    int mid=L+R>>1;
    if(x<=mid)ask(x,y,k<<1);
    if(y>mid)ask(x,y,k<<1|1);
    return ;
}
inline ll query(int x,int y){
    xt=yt=xyt=0;
    ask(x,y,1);
    xt%=mod,yt%=mod,xyt%=mod;
    return ((xt*yt-xyt*xyt)%mod+mod)%mod;
}
int main(){
    n=read(),m=read();
    build(1,n,1);
    while(m--){
        int opt=read(),x=read(),y=read();
        if(opt==1)change(id[x],y,read());
        else printf("%lld\n",query(x,y));
    }
    return 0;
}
View Code

T3

  • 李煜东的原题。
  • DP挺简单的,主要是路径的记录。
  • 如果路径记录只开一维则无法保证最终倒序寻出的路径在第一个序列上的有序。
  • 所以开2维,倒序求路径时保证记录路径的前驱数组中第一维单调不上升。
#include
using namespace std;
int const N=5001;
inline int read(){
    int ss(0);char bb=getchar();
    while(bb<48||bb>57)bb=getchar();
    while(bb>=48&&bb<=57)ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar();
    return ss;
}
int n,m,now;
int a[N],b[N];
short f[N][N],p[N][N];
inline int max(int x,int y){
    return x>y?x:y;
}
int main(){
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    n=read();
    for(register int i=1;i<=n;++i)a[i]=read();
    m=read(),now=a[1];
    for(register int i=1;i<=m;++i)
        if((b[i]=read())==now)f[1][i]=1;
    for(register int i=2,la,lp;i<=n;++i){
        la=lp=0;
        for(register int j=1,ap=a[i];j<=m;++j){
            if(b[j]==ap)p[i][j]=lp,f[i][j]=la+1;
            else f[i][j]=f[i-1][j];
            if(b[j]la)la=f[i][j],lp=j;
        } 
    }
    register int mx=0,mi=0,tp;
    for(register int i=1;i<=m;++i)
        if(f[n][i]>mx)mx=f[n][mi=i];
    a[tp=1]=mi;
    while(n&&mi)
        if(p[n][mi])a[++tp]=mi=p[n--][mi];
        else --n;
    printf("%d\n",mx);
    while(tp)printf("%d ",b[a[tp--]]);
    puts("");
    return 0;
}
View Code

你可能感兴趣的:(20191005)