NOIP模拟题 2016.11.8 (2) [线段树] [动态逆序对] [矩阵快速幂] [数论] [欧拉函数]

来源:互联网 发布:在线画板软件 编辑:程序博客网 时间:2024/06/06 04:27

T1:
题意:有一个序列,m次操作,每次操作指定一个位置,将当前位置和该位置后面所有比它小的数构成的子序列排序,放入原位置。求每次操作后,逆序对个数。

首先在线做法不好做,那么考虑离线。
对于一个数,它对逆序对个数贡献为0的时候,就是它之间比它大的数,最早的操作时间。
那么可以采用线段树维护这个最早时间,从前向后走一遍,边走边更新线段树并查询。
每次把这个数对应最早时间的答案减去当前后面比它小的数的个数,也就是当前数对逆序对的贡献。
树状数组求逆序对。。

#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<cmath>#include<vector>#include<queue>#include<stack>#include<map>#include<set>#include<string>#include<iomanip>#include<ctime>#include<climits>#include<cctype>#include<algorithm>#ifdef WIN32#define AUTO "%I64d"#else#define AUTO "%lld"#endifusing namespace std;#define smax(x,tmp) x=max((x),(tmp))#define smin(x,tmp) x=min((x),(tmp))#define maxx(x1,x2,x3) max(max(x1,x2),x3)#define minn(x1,x2,x3) min(min(x1,x2),x3)typedef long long LL;const int INF=0x3f3f3f3f;const int maxn = 100005;struct Node{    int val;    int dfn;}node[maxn];int n,m;int lim;int a[maxn];inline void init(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++) scanf("%d",&node[i].val),node[i].dfn=INF;    for(int i=1;i<=n;i++) a[i] = node[i].val;    sort(a+1,a+n+1);    lim = unique(a+1,a+n+1) - a - 1;    for(int i=1;i<=n;i++) node[i].val = lower_bound(a+1,a+lim+1,node[i].val) - a;    for(int i=1;i<=m;i++)    {        int x;        scanf("%d",&x);        if(node[x].dfn >= INF) node[x].dfn = i;    }}#define lowbit(x) ((x)&(-x))int tree[maxn];inline void add(int x,int val) { for(int i=x;i<=lim;i+=lowbit(i)) tree[i]+=val; }inline int Sum(int x){    int ret = 0;    for(int i=x;i;i-=lowbit(i)) ret += tree[i];    return ret;}int MIN[maxn<<2];inline void update(int root) { MIN[root] = min(MIN[root<<1],MIN[root<<1|1]); }int query(int root,int l,int r,int x,int y){    if(x<=l && r<=y) return MIN[root];    int mid = (l+r)>>1;    int min1(INF),min2(INF);    if(x<=mid && l<=y) min1 = query(root<<1,l,mid,x,y);    if(y>=mid+1 && r>=x) min2 = query(root<<1|1,mid+1,r,x,y);    return min(min1,min2);}void modify(int root,int l,int r,int pos,int val){    if(l == r)    {        smin(MIN[root],val);        return;    }    int mid = (l+r)>>1;    if(pos<=mid) modify(root<<1,l,mid,pos,val);    else modify(root<<1|1,mid+1,r,pos,val);    update(root);}LL delta[maxn];int rev[maxn];void work(){    LL ans = 0;    for(int i=n;i>=1;i--)    {        rev[i] = Sum(node[i].val-1);        ans += rev[i];        add(node[i].val,1);    }    memset(MIN,0x3f,sizeof(MIN));    for(int i=1;i<=n;i++)    {        modify(1,1,lim,node[i].val,node[i].dfn); // update first        int T = query(1,1,lim,node[i].val,lim); // query with itself        if(T >= INF) continue;        delta[T] += rev[i]; // if the same , calculate after modified    }    printf(AUTO"\n",ans);    for(int i=1;i<=m;i++)    {        ans -= delta[i];        printf(AUTO"\n",ans);    }}int main(){    freopen("A.in","r",stdin);    freopen("A.out","w",stdout);    init();    work();    return 0;}

T2:
题意:一开始有9个机器人分别站在0~8这九个位置上,每个机器人可以上下左右移动,也可以不动,求经过N步后,每个位置恰好有一个机器人的方案数。

很明显是矩阵快速幂。。。
构造转移矩阵通过abs(i%3-j%3) + abs(i/3-j/3) == 1来构造。
注意最后答案的求法,一共有9!种最终排列方式,那么对于每一种方式,用乘法原理得到当前终态的方案数,最后把所有方案数加起来即可。

#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<cmath>#include<vector>#include<queue>#include<stack>#include<map>#include<set>#include<string>#include<iomanip>#include<ctime>#include<cctype>#include<algorithm>#ifdef WIN32#define AUTO "%I64d"#else#define AUTO "%lld"#endifusing namespace std;#define smax(x,tmp) x=max((x),(tmp))#define smin(x,tmp) x=min((x),(tmp))#define maxx(x1,x2,x3) max(max(x1,x2),x3)#define minn(x1,x2,x3) min(min(x1,x2),x3)typedef long long LL;template <class T> inline void read(T &x){    x = 0;    T flag = 1;    char ch = (char)getchar();    while(ch<'0' || ch>'9')    {        if(ch == '-') flag = -1;        ch = (char)getchar();    }    while(ch>='0' && ch<='9')    {        x = (x<<1) + (x<<3) + ch - '0';        ch = (char)getchar();    }    x *= flag;}template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); }const int maxn = 10;const int N = 9;const int mod = 1000000007;struct Matrix{    LL w[maxn][maxn];    Matrix() { memset(w,0,sizeof(w)); }    Matrix operator * (const Matrix &t)    {        Matrix ret;        for(int i=0;i<N;i++)            for(int j=0;j<N;j++)                for(int k=0;k<N;k++)                    (ret.w[i][j] += w[i][k] * t.w[k][j] % mod) %=mod;        return ret;    }    void operator *= (Matrix &t) { (*this) = t * (*this); }};Matrix quick_exp(Matrix tmp,LL p){    Matrix ans;    for(int i=0;i<N;i++) ans.w[i][i]=1;    while(p)    {        if(p&1) ans *= tmp;        p >>= 1;        tmp *= tmp;    }    return ans;}int a[maxn];int main(){    freopen("B.in","r",stdin);    freopen("B.out","w",stdout);    LL n;    read(n);    Matrix delta;    for(int i=0;i<N;i++)        for(int j=0;j<N;j++)            if(abs(i%3-j%3) + abs(i/3-j/3) == 1) delta.w[i][j] = 1;    for(int i=0;i<N;i++) delta.w[i][i] = 1;    delta = quick_exp(delta,n);    for(int i=0;i<N;i++) a[i]=i;    LL ans = 0;    do    {        LL tmp = 1;        for(int i=0;i<N;i++) (tmp *= delta.w[i][a[i]]) %= mod;        (ans += tmp) %=mod;    }while(next_permutation(a,a+N));    printf(AUTO,ans);    return 0;}

T3:
题意:求gcd(i,j)的k次方的和。

0 0
原创粉丝点击