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次方的和。