Day1 T1 裸快速幂水题
//结果=(x+m*10^k)mod n #include <stdio.h> #define LL long long int LL n,m,k,x; LL fastPow(LL base,LL e) //base^e快速幂 { LL out; if(e==0) return 1; out=fastPow(10,e/2); out*=out; out%=n; if(e%2==1) {out*=base; out%=n;} return out; } int main() { LL i,j; scanf("%lld%lld%lld%lld",&n,&m,&k,&x); printf("%lld\n",(x+(m*fastPow(10,k))%n)%n); return 0; }
Day1 T2 贪心+逆序对,我用n次二分,也就是nlogn的算法,交了题发现只有60分有木有!
/* 题目思路:二分查找+线段树求逆序对 */ #include <stdio.h> #include <stdlib.h> #define MAXN 300000 #define MOD 99999997 struct node { int num,v; }a[MAXN],b[MAXN]; int c[MAXN],sum=0,n,tot[MAXN]; //sum=逆序对个数,aa是排序后的a数组,bb是排序后的b数组,c[i]=x表示a[i]移动到b数组中的x位置,tot[i]=结点i中 int pos,ql,qr; //pos=比对的基准位置,eg:tot[i]=结点i上,在c[pos]后面,且比c[pos]小的数的个数 int cmp(const void *x,const void *y) { struct node *xx=(node *)x; struct node *yy=(node *)y; return (((xx->v)<(yy->v))?1:-1); } void build(int o,int L,int R) //建立结点编号 { if(L==R) { if(L>pos&&c[L]<c[pos]) tot[o]=1; else tot[o]=0; return; } int M=L+(R-L)/2; build(o*2,L,M); build(o*2+1,M+1,R); tot[o]=tot[o*2]+tot[o*2+1]; tot[o]%=MOD; } int main() { int i,j; scanf("%d",&n); a[0].v=-1000; for(i=1;i<=n;i++) { scanf("%d",&a[i].v); a[i].num=i; } qsort(a,n+1,sizeof(a[0]),cmp); b[0].v=-1000; for(i=1;i<=n;i++) { scanf("%d",&b[i].v); b[i].num=i; } qsort(b,n+1,sizeof(b[0]),cmp); for(i=1;i<=n;i++) c[b[i].num]=a[i].num; //排序后,记录下b数组中的Bx在a数组中对应的映射Ax //n次建立线段树求逆序对T_T,复杂度nlogn for(i=1;i<=n;i++) { pos=i; build(1,1,n); //线段树初始化 sum+=tot[1]; sum%=MOD; } printf("%d\n",sum); return 0; }
后来发现虽然线段树和树状数组都是O(nlogn)的,但是树状数组常数小,于是写了树状数组版本的,AC
/* 题目思路:树状数组+归并排序求逆序对 */ #include <stdio.h> #include <stdlib.h> #include <algorithm> #define MAXN 300000 #define MOD 99999997 using namespace std; struct node { int num,h; //编号 高度 }a[MAXN],b[MAXN]; bool cmp(node a,node b) { return a.h<b.h; } int c[MAXN],tmp[MAXN],ans,n; //ans=逆序对个数 int line[MAXN]; int lowbit(int x) { return x&(-x); } void update(int o,int v) //对位置为o更新,增加v { while(o<=n) { line[o]+=v; o+=lowbit(o); } } int query(int o) //求1~o的和 { int sum=0; while(o>0) { sum+=line[o]; o-=lowbit(o); } return sum; } int main() { scanf("%d",&n); a[0].h=-1000; b[0].h=-1000; for(int i=1;i<=n;i++) { scanf("%d",&a[i].h); a[i].num=i; } for(int i=1;i<=n;i++) { scanf("%d",&b[i].h); b[i].num=i; } sort(a+1,a+n+1,cmp); //排序 sort(b+1,b+n+1,cmp); for(int i=1;i<=n;i++) c[a[i].num]=b[i].num; //记录下Ax的映射Bx,即排序后,a数组中第i个元素对应b数组中第i个元素,记录下a数组中每个编号的元素的b数组中对应元素编号 //要求出最少移动次数,求c数组的逆序对即可,简化过程,一个数组不动,只动另一个数组,逆序对的个数含义就是使c数组变成非递降序列的最少次数 for(int i=1;i<=n;i++) { update(c[i],1); ans+=i-query(c[i]); ans%=MOD; } printf("%d\n",ans%MOD); return 0; }
Day2 T1 模拟水题,文艺青年:贪心 普通青年:线段树 2B青年:打表