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

20 篇文章 0 订阅
12 篇文章 0 订阅

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"
#endif
using 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"
#endif
using 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值