逆序对数:
链接:https://ac.nowcoder.com/acm/contest/358/D
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld
出题人的妹子送了出题人一个手环,这个手环上有 n 个珠子,每个珠子上有一个数。
有一天,出题人和妹子分手了,想把这个手环从两个珠子间切开,并按顺时针顺序展开成一条链。
可以发现,这条链一共有 n 种可能性。求这 n 种可能性的逆序对数之积模 1000000007。
解析:
数据非常大,要进行离散化
先算出第一种情况的逆序对个数,然后再最后的呢个数换到前面的情况
前面比它小的会成逆序对,比他大的会失去逆序对
用树状数组求原数组逆序对数,然后我们模拟将a[1]换到a[n]后面
对于比a[1]后面比a[1]小的数将成新的逆序对,比a[1]的的数将失去逆序对
我们标记a[i]出现次数,然后用前缀和维护数目,实现o(1)查询,遍历累乘即可
ac:
#include
#define ll long long
using namespace std;
#define MAXN 200000+5
#define mod 1000000007
ll a[MAXN],b[MAXN],n;
ll tree[MAXN],vis[MAXN],sum[MAXN];
ll lowbit(ll x)
{
return x&(-x);
}
void add(ll x,int val)
{
for(ll i=x;i<=n;i=i+lowbit(i))
{
tree[i]+=val;
tree[i]=tree[i]%mod;
}
}
ll get(ll x)
{
ll ans=0;
for(ll i=x;i;i=i-lowbit(i))
{
ans+=tree[i];
}
return ans%mod;
}
int main()
{
memset(vis,0,sizeof(vis));
memset(tree,0,sizeof(tree));
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(sum,0,sizeof(sum));
scanf("%lld",&n);
for(ll i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
b[i]=a[i];
}
sort(b+1,b+1+n);
ll cnt=unique(b+1,b+1+n)-b-1;
for(ll i=1;i<=n;i++)
{
a[i]=lower_bound(b+1,b+1+cnt,a[i])-b;
vis[a[i]]++;
}
for(ll i=1;i<=n;i++)
sum[i]=sum[i-1]+vis[i];
ll ans=0;
for(ll i=1;i<=n;i++)
{
add(a[i],1);
ans+=i-get(a[i]);
ans=ans%mod;
}
ll bans=ans,aq,bq;
for(ll i=n;i>=2;i--)
{
aq=sum[a[i]-1];//比a[i]小的
bq=n-sum[a[i]];//比a[i]大的
bans=(bans+aq-bq+mod)%mod;
ans=ans*bans;
ans=ans%mod;
}
printf("%lld\n",ans);
return 0;
}
区间小于等于k的数目(离线):
树状数组:
a[i]按val排,b[i]按val(k)排,保存和a[i]的位置,b[i]的顺序,保证单调
每次只add比b[i].val小的a[i].val的位置
每次把比b[i]小的数位置上+1,这样我们直接查询(l,r)的数目,就是(l,r)中小于b[i].val的数目
这里吧所有比b[i]小的数都+1,保证在(l,r)区间内查询到的值是完整的
#include
#define MAXN 100005
#define ll long long
#define lowbit(x) ((x)&(-x))
using namespace std;
int n,m;
int ans[MAXN];
struct node
{
int id,val;
friend bool operator <(node a,node b)
{
return a.val0;i-=lowbit(i))
ans+=sum[i];
return ans;
}
int main()
{
int t,cas=1;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(sum,0,sizeof(sum));
memset(ans,0,sizeof(ans));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].val);
a[i].id=i;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&b[i].l,&b[i].r,&b[i].val);
b[i].l++;
b[i].r++;
b[i].id=i;
}
sort(a+1,a+n+1);
sort(b+1,b+m+1);
for(int i=1,j=1;i<=m;i++)
{
while(j<=n&&a[j].val<=b[i].val)
{
add(a[j].id,1);
j++;
}
ans[b[i].id]=query(b[i].r)-query(b[i].l-1);
}
printf("Case %d:\n",cas++);
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
}
return 0;
}
区间不同数的个数:
我们仅对a[i]的值的最后一个位置标记,就能通过查询(l,r)的和得区间不同数的个数
查询操作按r排序,a[i]不同排序,但是每次查询(l,r)时,我们限制a[i]的i小于等于r,最多插入n次
我们每次查询的时候,把(1,r)的a[i]都标记了,但是对于值相同的a[i],我们仅仅标记最后的位置,一种值最多产生一次贡献
对于值相同的a[i],我们只会计算一次产生的影响.我们查询的r是单调增的,插入的位置也是单调增的,保证正确性
#include
#define lowbit(x) (x)&(-x)
#define ll long long
#define MAXN 400005
using namespace std;
ll a[MAXN],n,m;
unordered_map vis;
ll sum[MAXN];
ll ans[MAXN];
void add(ll x,ll c)
{
for(ll i=x;i<=n;i+=lowbit(i))
sum[i]+=c;
}
ll query(ll x)
{
ll ans=0;
for(ll i=x;i;i-=lowbit(i))
ans+=sum[i];
return ans;
}
struct node
{
ll l,r,id;
friend bool operator <(node a,node b)
{
return a.r
http://codeforces.com/contest/301/status/page/1?order=BY_JUDGED_DESC
区间非互质对数
题意:
#include
#include
#include
#include
using namespace std;
const int MAXN = 200010;
int a[MAXN], pos[MAXN], tree[MAXN];
int n, m;
int lowbit(int x) {
return x & (-x);
}
int get_sum(int k) {
int ans = 0;
while(k > 0) {
ans += tree[k];
k -= lowbit(k);
}
return ans;
}
void modify(int k, int val) {
while(k <= n) {
tree[k] += val;
k += lowbit(k);
}
}
struct QUERY {
int id, L, R;
bool operator < (const QUERY &rhs) const {
return L < rhs.L;
}
} query[MAXN], query_t[MAXN];
int ans[MAXN];
int main()
{
int g=0;
for(int i=0;i<10;i++)
g++;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
{
scanf("%d", &a[i]);
pos[a[i]] = i;
}
for(int i = 1; i <= m; ++i)
{
scanf("%d%d", &query[i].L, &query[i].R);
query_t[i].L = query[i].R;
query_t[i].R = query[i].L;
query[i].id = query_t[i].id = i;
}
sort(query + 1, query + m + 1);
sort(query_t + 1, query_t + m + 1);
for(int i = 1, j = 1, k = 1; i <= n; ++i)
{
while(j <= m && query[j].L == i) {
ans[query[j].id] -= get_sum(query[j].R) - get_sum(query[j].L - 1);
++j;
}
for(int p = a[i]; p <= n; p += a[i]) modify(pos[p], 1);
while(k <= m && query_t[k].L == i) {
ans[query_t[k].id] += get_sum(query_t[k].L) - get_sum(query_t[k].R - 1);
++k;
}
}
for(int i = 1; i <= m; ++i)
printf("%d\n", ans[i]);
}
区间(1,a)+(b,n)不同数的个数:
#include
#define MAXN 200005
using namespace std;
int a[MAXN],vis[MAXN],c[MAXN],gg[MAXN],ans[MAXN];
int mp[MAXN];
int n;
int lowbit(int x){
return x&(-x);
}
struct node
{
int l,r;
int id;
bool friend operator <(node a,node b)
{
return a.r0)
{
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
int main()
{
int m,la,ra;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=0;i=1)
g++;
int j=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&la,&ra);
if(ra-la<=1)
{
ans[i]=g;
continue;
}
q[++j].l=la+1;
q[j].r=ra-1;
q[j].id=i;
}
sort(q+1,q+j+1);
int k=j,tt=1;
for(int i=1;i<=k;i++)
{
for(int j=tt;j<=q[i].r;j++)
{
if(vis[a[j]]==0)
{
mp[a[j]]=j;//只标记一次
vis[a[j]]++;
}
else{
vis[a[j]]++;
}
if(vis[a[j]]==gg[a[j]])//如果出现次数==总次数,呢么就删完了
{
add(mp[a[j]],1);//对标记点-1
}
}
tt=q[i].r+1;
ans[q[i].id]=g-(query(q[i].r)-query(q[i].l-1));
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
}
return 0;
}
http://acm.hdu.edu.cn/showproblem.php?pid=6609
在尾部逐步插入n个元素,求插入第i个元素时,[1,i)内删去多少个元素,可使前缀和[1,i]不大于m
离散化数据,这里数据不多,不去重,方便操作
二分最多可以留下多少个,即得最少减去的数,用树状数组求和
ac:
#include
#define lowbit(x) (x)&(-x)
#define MAXN 800005
#define ll long long
using namespace std;
ll n,m;
ll a[MAXN],b[MAXN];
ll sum[MAXN];
ll num[MAXN];
ll val[MAXN];
map hash1;
void add(ll x,ll c)
{
for(ll i=x;i<=n;i+=lowbit(i))
{
sum[i]+=c,num[i]+=1;
}
}
ll query(ll r)
{
ll ans=0;
for(ll i=r;i>0;i-=lowbit(i))
ans+=sum[i];
return ans;
}
ll query2(ll r)
{
ll ans=0;
for(ll i=r;i>0;i-=lowbit(i))
ans+=num[i];
return ans;
}
ll solve(ll i)//二分最多可以留下多少个,即得最少减去的数
{
ll l=1,r=n;
ll ans=0;
while(l<=r)
{
ll mid=(l+r)>>1;
if(m-val[a[i]]>=query(mid))
{
l=mid+1;
ans=max(ans,mid);
}
else{
r=mid-1;
}
}
ll ans2=query2(ans);
return i-1-ans2;
}
int main()
{
ll t;
scanf("%lld",&t);
while(t--)
{
hash1.clear();
memset(sum,0,sizeof(sum));
memset(num,0,sizeof(num));
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]),b[i]=a[i];
sort(b+1,b+1+n);
for(ll i=1;i<=n;i++)//这里离散化不去重
{
ll k=a[i];
a[i]=lower_bound(b+1,b+1+n,a[i])-b+hash1[a[i]];//同值的不同数在不同的位置
hash1[k]++;//累计数目
val[a[i]]=k;//给位置标记权值
}
for(ll i=1;i<=n;i++)
{
printf("%lld ",solve(i));
add(a[i],val[a[i]]);
}
printf("\n");
}
return 0;
}
https://nanti.jisuanke.com/t/A1253
题意:
给定2个串,A,B
q次询问
1.询问A的子串(l,r)中,出现多少次B
2.修改A串的第x个字母,修改为C
解析:
直接树状数组暴力处理
注意合法范围
如果修改x,则会对(max(1,x-m+1),min(n,x+m-1))区间产生影响
#include
#define lowbit(x) (x)&(-x)
#define MAXN 1000005
using namespace std;
char ss[MAXN];
char cc[11];
int n;
int sum[MAXN];
void init()
{
memset(sum,0,sizeof(sum));
}
void add(int x,int v)
{
for(int i=x;i<=n;i+=lowbit(i))
sum[i]+=v;
}
int query(int x)
{
int ans=0;
for(int i=x;i>0;i-=lowbit(i))
ans+=sum[i];
return ans;
}
char qq[5];
int main()
{
int t,q,l,r,x;
scanf("%d",&t);
while(t--)
{
init();
scanf("%d",&q);
scanf("%s",ss+1);
scanf("%s",cc+1);
n=strlen(ss+1);
int m=strlen(cc+1);
for(int i=1;i+m-1<=n;i++)
{
int sign=0;
for(int j=1;j<=m;j++){
if(ss[i+j-1]!=cc[j]){
sign=1;
break;
}
}
if(sign==0)
add(i,1);
}
while(q--)
{
scanf("%s",&qq);
if(qq[0]=='Q'){
scanf("%d %d",&l,&r);
if(r-l+1>=m)
printf("%d\n",query(r-m+1)-query(l-1));
else
printf("0\n");
}
else{
scanf("%d %s",&x,&qq);
ss[x]=qq[0];
int st=max(1,x-m+1);
int ed=min(n,x+m-1);
for(int i=st;i<=ed;i++)
{
int sign=0;
for(int j=1;j<=m;j++){
if(ss[i+j-1]!=cc[j]){
sign=1;
break;
}
}
int v=query(i)-query(i-1);
if(sign==0&&v==0)
add(i,1);
else if(sign==1&&v==1)
add(i,-1);
}
}
}
printf("\n");
}
return 0;
}