2017/10/6模拟赛总结

来源:互联网 发布:淘宝卖家发布宝贝流程 编辑:程序博客网 时间:2024/06/01 19:18

题目来自NOIP2016十连测第6场
过了若干天才填完坑..
不得不说这一场题目真是太巧(bian)妙(tai)了

T1 幻想

所有数据都很大= =
首先看到h(i)是一个乱七八糟的东西 显然没有什么规律
那么应该就只能枚举了
正好多组数据一共枚举最多108
分析Si 对于每一个可以用O(logki)的时间递归求出
然而枚举就要求必须是O(1)求出了
打表找规律!
k=3
012 120 201 120 201 012 201 012 120…
1 1 2 1 1 2 1 1
先把它看做每次加上1然后%k
这样会有一些位置多加了一些数
然后把这些位置的k进制打出来
就会发现 k进制下后面有多少连续的0 在这一位就会加上几
于是可以对于每组数据 先求出第Li个位置的值
然后从LiRi模拟k进制加法 每次求出最后有几个0即可
O(i=1T(RiLi+TlogkiRi))
由于过于逼近时限 需要卡常
比如取地址比运算快 register等黑科技
因此代码就异常的丑=

#include<bits/stdc++.h>using namespace std;#define Add(x) x=inc[x]#define ADD(x) x=Inc[x]#define mo 20000116int k;int calc(long long x,long long base){    if (base==1) return x-1;    else return (calc((x-1)%base+1,base/k)+(x-1)/base)%k;}int main(){    int inc[1010],Inc[1010],A[60],Case;    register int j;    scanf("%d",&Case);    for (j=0;j<1000;j++) inc[j]=Inc[j]=j+1;    while (Case--){        long long l,r;        scanf("%d%lld%lld",&k,&l,&r);        inc[k-1]=0;        register long long i,base=1,x=l;        while (base*k<l+1) base*=k;        register int t=calc(l+1,base),p=l%mo;        memset(A,0,sizeof(A));        j=1;        while (x){            A[j]=x%k;            x/=k;            ADD(j);        }        register unsigned int ans=(1LL*p*p+l+804)/233*t;        for (i=l+1;i<=r;i++){            j=1;            Add(t);            while (A[j]==k-1){                A[j]=0;                Add(t);                ADD(j);            }            ADD(A[j]);            p++;            if (p==mo) p=0;            ans+=(1LL*p*p+i+804)/233*t;        }        printf("%u\n",ans);        inc[k-1]=k;    }    return 0;}

T2 告别

一个比较简单的想法是用dpi,j表示第i天 当前排列状态为j的概率
由于状态有n!种 很难存下
考虑优化状态的设计
每天的操作是把三元组置换一遍 因此排列里的具体数字是无关紧要的 只和数字的关系有关
如果依照置换的方式定义 把不在自己位置上的数字连到自己位置上 就会形成若干个环
例:2 3 1 4 6 5 中有 2-3-1 4 6-5 三个环
因此可以依据环的大小和数量定义状态
用map映射一下即可
打表发现最多不会超过140个本质不同的状态
然后就可以用矩阵快速幂转移dp了
因为所有环的本质是一样的 转移的时候随便造出一个满足的排列即可
由于一个置换可能对应很多排列 直接做不能保证正确性
观察到1,2,3,,n这个排列是唯一对应的 因此我们先把B数组映射为1,2,3,,n
O(1403log2m)
然后由于是计算概率 dp中用到了逆元
另外可以把vector映射用Trie树替代

讲道理这dp也太鬼畜了吧

#include<bits/stdc++.h>using namespace std;#define N 20#define M 140#define mo 998244353map<vector<int>,int>mp;vector<int>A,P[M];int tot,n,m,a[N],b[N],id[N],B[N];bool vis[N];struct Matrix{    int s[M][M];    Matrix(){memset(s,0,sizeof(s));}    friend Matrix operator *(Matrix A,Matrix B){        Matrix C;        int i,j,k;        for (i=1;i<M;i++)            for (j=1;j<M;j++)                if (A.s[i][j])                    for (k=1;k<M;k++)                        C.s[i][k]=(C.s[i][k]+1LL*A.s[i][j]*B.s[j][k])%mo;        return C;    }}I;void Pow(Matrix &G,int y){    Matrix tmp=I;    while (y){        if (y&1) G=G*tmp;        y>>=1;        tmp=tmp*tmp;    }}void dfs(int l,int sum){    if (sum==n){        mp[A]=++tot;        P[tot]=A;        return;    }    int i;    for (i=l;i<=n-sum;i++){        A.push_back(i);        dfs(i,sum+i);        A.pop_back();    }}void To_Array(int x,int *G){    A=P[x];    int i,j,k=1;    for (i=0;i<A.size();i++){        for (j=0;j<A[i]-1;j++) G[j+k]=j+k+1;        G[j+k]=k;        k+=A[i];    }}int To_Vector(int *G){    A.clear();    memset(vis,0,sizeof(vis));    int i;    for (i=1;i<=n;i++){        if (vis[i]) continue;        int cnt=0,x=i;        do{            vis[x]=1;            x=G[x];            cnt++;        }while (x!=i);        A.push_back(cnt);    }    sort(A.begin(),A.end());    return mp[A];}int Inv(int x){    int res=1,y=mo-2;    while (y){        if (y&1) res=1LL*res*x%mo;        y>>=1;        x=1LL*x*x%mo;    }    return res;}void Init(){    A.clear();    dfs(1,0);    int i,j,k,l,all=Inv(n*(n-1)*(n-2));    I.s[1][1]=1;    for (i=2;i<=tot;i++)        for (j=1;j<=n;j++)            for (k=1;k<=n;k++){                if (j==k) continue;                for (l=1;l<=n;l++){                    if (j==l || k==l) continue;                    To_Array(i,B);                    int tmp=B[j]; B[j]=B[k]; B[k]=B[l]; B[l]=tmp;                    int t=To_Vector(B);                    I.s[i][t]=(I.s[i][t]+all)%mo;                }            }}int main(){    scanf("%d%d",&n,&m);    Init();    int i;    for (i=1;i<=n;i++) scanf("%d",&a[i]);    for (i=1;i<=n;i++) scanf("%d",&b[i]),id[b[i]]=i;    for (i=1;i<=n;i++) a[i]=id[a[i]];    Matrix dp;    dp.s[1][To_Vector(a)]=1;    Pow(dp,m);    printf("%d\n",dp.s[1][1]);    return 0;}

T3 现实

首先分析一下模型
答案一定在环上
把一个点分成入点和出点 之后一条边断开可以成立就代表了一个点成立
先找一个环出来
如果找不到环 所有点都可以当做答案
然后拓扑排序一波
如果还有环 就说明无解
这之后就会发现有两种环套环的情况
X代表不能作为答案
1
2
根据环的不同方向 找出向左和向右可以到达的最大/最小编号
代码中Mn/Mx对应第1种 Mx1对应第2种
分类讨论即可
看起来只需要求Mn即可 但是会出现这种情况
3
右边的点Mn会变成最左边的点 而事实上中间的那个点也不能作为答案
所以就会有Mx 访问到中间那个点时就会排除这种情况
O(n)

#include<bits/stdc++.h>using namespace std;#define N 1000010struct edge{    int nxt,t;}e[N<<2];int n,m,gn,head[N],edge_cnt,vis[N],fa[N],F[N],Cir[N],cir_cnt,ans[N],ans_cnt,Mx[N],Mn[N],Mx1[N],Deg[N],Q[N],sum[N];bool used[N<<2],Find;void add_edge(int x,int y){    e[edge_cnt]=(edge){head[x],y};    head[x]=edge_cnt++;}void dfs(int x){    vis[x]=1;    int i;    for (i=head[x];~i;i=e[i].nxt){        int to=e[i].t;        if (!vis[to]){            fa[to]=x;            F[to]=i;            dfs(to);        }        else        if (vis[to]==1){            int y=x;            while (y!=to){                Cir[++cir_cnt]=y;                used[F[y]]=1;                y=fa[y];            }            Cir[++cir_cnt]=to;            Find=1;        }        if (Find) return;    }    vis[x]=2;}bool Top(){    int l=1,r=0,i,j;    for (i=1;i<=gn;i++)        for (j=head[i];~j;j=e[j].nxt)            if (!used[j]) Deg[e[j].t]++;    for (i=1;i<=gn;i++)        if (!Deg[i]) Q[++r]=i;    while (l<=r){        int x=Q[l++];        for (i=head[x];~i;i=e[i].nxt){            if (used[i]) continue;            int to=e[i].t;            Deg[to]--;            if (!Deg[to]) Q[++r]=to;        }    }    return r==gn;}   void solve(){    int i,j;    for (i=1;i<=n;i++)        if (!vis[i]){            dfs(i);            if (Find) break;        }    if (!Find){        for (i=1;i<=n;i++) ans[++ans_cnt]=i;        return;    }    if (!Top()) return;    memset(Mn,63,sizeof(Mn));    reverse(Cir+1,Cir+cir_cnt+1);    for (i=1;i<=cir_cnt;i++) Mn[Cir[i]]=Mx[Cir[i]]=Mx1[Cir[i]]=i;    for (i=gn;i;i--){        int x=Q[i];        for (j=head[x];~j;j=e[j].nxt){            if (used[j]) continue;            int to=e[j].t;            Mn[x]=min(Mn[x],Mn[to]);            Mx1[x]=max(Mx1[x],Mx1[to]);        }    }    for (i=1;i<=gn;i++){        int x=Q[i];        for (j=head[x];~j;j=e[j].nxt){            if (used[j]) continue;            int to=e[j].t;            Mx[to]=max(Mx[to],Mx[x]);        }    }    for (i=1;i<=cir_cnt;i++){        if (Mn[Cir[i]]<i){            sum[1]++;            sum[Mn[Cir[i]]+1]--;            sum[i+1]++;        }        if (Mx[Cir[i]]>i){            sum[1]++;            sum[i]--;            sum[Mx[Cir[i]]+1]++;        }        if (Mx1[Cir[i]]>i){            sum[i+1]++;            sum[Mx1[Cir[i]]+1]--;        }    }    int tmp=0;    for (i=1;i<=cir_cnt;i++){        tmp+=sum[i];        if (!tmp && Cir[i]>n) ans[++ans_cnt]=Cir[i]-n;    }}       int main(){    memset(head,-1,sizeof(head));    scanf("%d%d",&n,&m);    gn=n*2;    int i;    for (i=1;i<=n;i++) add_edge(i,i+n);    for (i=1;i<=m;i++){        int x,y;        scanf("%d%d",&x,&y);        add_edge(x+n,y);    }    solve();    printf("%d\n",ans_cnt);    sort(ans+1,ans+1+ans_cnt);    for (i=1;i<=ans_cnt;i++) printf("%d ",ans[i]);    return 0;}

Date:2017/10/11
By CalvinJin