第二次参加USACO 本来打算2016-2017全勤的 January的好像忘记打了 听群里有人讨论才想起来
铂金组三题很有意思,都是两个排列的交叉对问题 我最后得分889/1000(真的菜)

T1.Why Did the Cow Cross the Road
题目大意:给出两个N个排列(N<=100,000),允许把其中一个排列循环移动任意位,a[i]表示i在第一个排列中的位置,b[i]表示第二个,定义交叉对(i,j)满足a[i]<a[j]且b[i]>b[j],求最少交叉对。
思路:数字大小没有影响,于是令第一个排列中第一个出现的编号为1,第二个出现的编号为2,于是变成最小化第二个排列的逆序对,一开始的逆序对可以O(nlogn)求出,然后考虑把当前第一个数移到最后,若这个数编号为x,明显多了n-x个逆序对,少了x-1个逆序对,就可以O(n)求出所有循环移动第二个排列的情况,然后我开心的交了,开心的WA了,后来才知道,自己忘记考虑移第一个的情况了(其实只要反过来再做一遍)。
得分:8/10(幸好数据大多是移第二个的?)

#include<cstdio>
#include<iostream>
using namespace std;
char B[<<],*S=B,C;int X;
inline int read()
{
while((C=*S++)<''||C>'');
for(X=C-'';(C=*S++)>=''&&C<='';)X=(X<<)+(X<<)+C-'';
return X;
}
#define MN 100000
#define N 131072
int f[MN+],a[MN+],t[N*];
void inc(int k){for(k+=N;k;k>>=)++t[k];}
int query(int l,int r)
{
int res=;
for(l+=N-,r+=N+;l^r^;l>>=,r>>=)
{
if(~l&)res+=t[l+];
if( r&)res+=t[r-];
}
return res;
}
int main()
{
freopen("mincross.in","r",stdin);
freopen("mincross.out","w",stdout);
fread(B,,<<,stdin);
int n=read(),i;long long ans,cnt=;
for(i=;i<=n;++i)f[read()]=i;
for(i=;i<=n;++i)cnt+=query(a[i]=f[read()],n),inc(a[i]);
for(ans=cnt,i=;i<n;++i)if((cnt+=n-(a[i]<<)+)<ans)ans=cnt;
cout<<ans;
}

AC代码

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
char B[<<],*S=B,C;int X;
inline int read()
{
while((C=*S++)<''||C>'');
for(X=C-'';(C=*S++)>=''&&C<='';)X=(X<<)+(X<<)+C-'';
return X;
}
#define MN 100000
#define N 131072
int c[MN+],d[MN+],f[MN+],a[MN+],t[N*];
void inc(int k){for(k+=N;k;k>>=)++t[k];}
int query(int l,int r)
{
int res=;
for(l+=N-,r+=N+;l^r^;l>>=,r>>=)
{
if(~l&)res+=t[l+];
if( r&)res+=t[r-];
}
return res;
}
int main()
{
freopen("mincross.in","r",stdin);
freopen("mincross.out","w",stdout);
fread(B,,<<,stdin);
int n=read(),i;long long ans,cnt=;
for(i=;i<=n;++i)f[c[i]=read()]=i;
for(i=;i<=n;++i)cnt+=query(a[i]=f[d[i]=read()],n),inc(a[i]);
for(ans=cnt,i=;i<n;++i)if((cnt+=n-(a[i]<<)+)<ans)ans=cnt;
for(i=;i<=n;++i)f[d[i]]=i;
memset(t,cnt=,sizeof(t));
for(i=;i<=n;++i)cnt+=query(a[i]=f[c[i]],n),inc(a[i]);
for(ans=min(ans,cnt),i=;i<n;++i)if((cnt+=n-(a[i]<<)+)<ans)ans=cnt;
cout<<ans;
}

T2.Why Did the Cow Cross the Road II
题目大意:两个排列排成两排(长度<=100,000),两排间相差不超过4的可以连边,边不能有交叉,问最多连几条。
思路:金组也有这道,不过N只有1000,考虑DP,f[i][j]表示第一个排列用到第i个 ,第二个排列用到第j个,最多连几条边,f[i][j]=max(f[i-1][j],f[i][j-1]),若a[i]和b[j]相差不超过4,f[i][j]=max(f[i][j],f[i-1][j-1]+1),就能O(n^2)完成。由于对于每个a[i],相差不超过4的b[j]的最多9个,我们把状态的j改成用了j,每个f[i]相对f[i-1]只变了9个,然后拿线段树维护一下就可以O(9nlogn)来DP了。
得分:10/10

#include<cstdio>
#include<algorithm>
using namespace std;
char B[<<],*S=B,C;int X;
inline int read()
{
while((C=*S++)<''||C>'');
for(X=C-'';(C=*S++)>=''&&C<='';)X=(X<<)+(X<<)+C-'';
return X;
}
#define MN 100000
#define N 131072
int a[MN+],b[MN+],t[N*],g[];
void renew(int k,int x){for(k+=N;k;k>>=)t[k]=max(t[k],x);}
int query(int l,int r)
{
int res=;
for(l+=N-,r+=N+;l^r^;l>>=,r>>=)
{
if(~l&)res=max(res,t[l+]);
if( r&)res=max(res,t[r-]);
}
return res;
}
int main()
{
freopen("nocross.in","r",stdin);
freopen("nocross.out","w",stdout);
fread(B,,<<,stdin);
int n=read(),i,j;
for(i=;i<=n;++i)a[i]=read();
for(i=;i<=n;++i)b[read()]=i;
for(i=;i<=n;++i)
{
for(j=-;j<=;++j)if(a[i]+j>&&a[i]+j<=n)g[j+]=query(,b[a[i]+j]-)+;
for(j=-;j<=;++j)if(a[i]+j>&&a[i]+j<=n)renew(b[a[i]+j],g[j+]);
}
printf("%d",query(,n));
}

T3.Why Did the Cow Cross the Road III
题目大意:两个排列(长度<=100,000),相等的连边,相差超过K的称为不友好,求不友好交叉对。
思路:我比较菜,只会暴力,考虑对每对相等的计算答案,每对a[i]=b[j],把a[i]左边设成1,右边设成0,b[j]同样,与这条边有交叉的边必然被编成一个1,1个0,也就是异或为1,考虑统计答案,做法1:把每个值现在的两个编码的异或值用树状数组维护,把每条边的当成一个二维询问,考虑莫队,插入和删除一个值都相当于令这个值编码的异或值异或上1,复杂度O(n^1.5logn);做法2:用可持久化bitset搞出两个排列所有前缀后缀的bitset,bitset里存每个值的编码,每条边都拿出bitset异或一下,复杂度O(n^2/32)。我比较懒,写了莫队,一开始T了六七个,调了下块大小,最后在K=3n^0.5左右只T两个点,玄妙无比。另外用sum(n)-sum(i+k)统计貌似比sum(i-k-1)多过了一个点,原理不明。
得分:13/15

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
char B[<<],*S=B,C;int X;
inline int read()
{
while((C=*S++)<''||C>'');
for(X=C-'';(C=*S++)>=''&&C<='';)X=(X<<)+(X<<)+C-'';
return X;
}
#define MN 100000
#define K 1050
#define lb(x) (x&-x)
int a[MN+],b[MN+],f[MN+],s[MN+];
struct query{int a,b,x;}q[MN+];
bool cmp(query a,query b){return a.a/K==b.a/K?a.b<b.b:a.a<b.a;}
int sum(int x){int r=;for(;x;x-=lb(x))r+=s[x];return r;}
void rev(int x){int r=(f[x]^)-f[x];f[x]^=;for(;x<=MN;x+=lb(x))s[x]+=r;}
int main()
{
freopen("friendcross.in","r",stdin);
freopen("friendcross.out","w",stdout);
fread(B,,<<,stdin);
int n,k,i,pa=,pb=;long long ans=;
n=read();k=read();
for(i=;i<=n;++i)a[i]=read(),q[a[i]].a=i,q[a[i]].x=a[i]+k;
for(i=;i<=n;++i)b[i]=read(),q[b[i]].b=i;
sort(q+,q+n+,cmp);
for(i=;i<=n;++i)
{
if(q[i].x>=n)continue;
while(pa<q[i].a)rev(a[++pa]);
while(pa>q[i].a)rev(a[pa--]);
while(pb<q[i].b)rev(b[++pb]);
while(pb>q[i].b)rev(b[pb--]);
ans+=sum(n)-sum(q[i].x);
}
cout<<ans;
}

正解:把每个数字在两个排列中的位置抽象到二维平面上(例如1在第一个排列中排第2个,在第二个排列中排第3,则用(2,3)表示),那么若两个数字交叉,则其中一个数字在二维平面上必然在另一个点的左上方(一维小,另一维大),要统计不友好交叉对,对每个点i统计1~i-k-1,i+k+1~n中有多少个点在他左上就好了,考虑把两个分开处理,可以依次插入点i,询问点i+k+1,插入点i+1,询问点i+k+2……另一个反过来再做一遍就能算出答案。这个问题可以用cdq分治或者二维线段树解决,复杂度O(nlogn^2)。

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
#define ll long long
char B[<<],*S=B,C;int X;
inline int read()
{
while((C=*S++)<''||C>'');
for(X=C-'';(C=*S++)>=''&&C<='';)X=(X<<)+(X<<)+C-'';
return X;
}
#define MN 100000
int a[MN+],b[MN+],t[MN*+],x[MN*+],y[MN*+],s[MN+];ll ans;
struct query{int x,y,t;}q[MN*+];
bool cmp(query a,query b){return a.x<b.x;}
void inc(int x,int z){for(;x<=MN;x+=x&-x)s[x]+=z;}
int sum(int x){int r=;for(;x;x-=x&-x)r+=s[x];return r;}
void solve(int l,int r)
{
if(l>=r)return;
int i,mid=l+r>>,qn;
for(i=l,qn=;i<=mid;++i)if(t[i])q[++qn]=(query){x[i],y[i],t[i]};
for(i=mid;++i<=r;)if(!t[i])q[++qn]=(query){x[i],y[i],t[i]};
sort(q+,q+qn+,cmp);
for(i=;i<=qn;++i)
if(q[i].t)inc(q[i].y,);
else ans+=sum(MN)-sum(q[i].y);
for(i=;i<=qn;++i)if(q[i].t)inc(q[i].y,-);
solve(l,mid);solve(mid+,r);
}
int main()
{
freopen("friendcross.in","r",stdin);
freopen("friendcross.out","w",stdout);
fread(B,,<<,stdin);
int n,k,i,cnt;
n=read();k=read();
for(i=;i<=n;++i)a[read()]=i;
for(i=;i<=n;++i)b[read()]=i;
for(cnt=,i=;i+k+<=n;++i)
{
x[++cnt]=a[i];y[cnt]=b[i];t[cnt]=;
x[++cnt]=a[i+k+];y[cnt]=b[i+k+];t[cnt]=;
}
solve(,cnt);
for(cnt=,i=n;i-k-;--i)
{
x[++cnt]=a[i];y[cnt]=b[i];t[cnt]=;
x[++cnt]=a[i-k-];y[cnt]=b[i-k-];t[cnt]=;
}
solve(,cnt);
cout<<ans;
fclose(stdin);fclose(stdout);return ;
}

USACO 2017 February Platinum的更多相关文章

  1. USACO 2017 February Gold

    那天打cf前无聊练手 T1.Why Did the Cow Cross the Road 题目大意:N*N的矩阵,从左上角走到右下角,走一步消耗T,每走3步消耗当前所在位置上的权值,求最小消耗 思路: ...

  2. USACO 2017 January Platinum

    因为之前忘做了,赶紧补上. T1.Promotion Counting 题目大意:给定一个以1为根的N个节点的树(N<=100,000),每个节点有一个权值,对于每个节点求出权值比它大的子孙的个 ...

  3. USACO 2017 FEB Platinum mincross 可持久化线段树

    题意 上下有两个位置分别对应的序列A.B,长度为n,两序列为n的一个排列.当Ai == Bj时,上下会连一条边.你可以选择序列A或者序列B进行旋转任意K步,如 3 4 1 5 2 旋转两步为 5 2 ...

  4. USACO 2017 FEB Platinum nocross DP

    题目大意 上下有两个长度为n.位置对应的序列A.B,其中数的范围均为1~n.若abs(A[i]-B[j]) <= 4,则A[i]与B[j]间可以连一条边.现要求在边与边不相交的情况下的最大的连边 ...

  5. Usaco 2019 Jan Platinum

    Usaco 2019 Jan Platinum 要不是昨天老师给我们考了这套题,我都不知道usaco还有铂金这么一级. 插播一则新闻:杨神坚持认为铂金比黄金简单,原因竟是:铜 汞 银 铂 金(金属活动 ...

  6. POJ1944 Fiber Communications (USACO 2002 February)

    Fiber Communications 总时间限制:  1000ms 内存限制:  65536kB 描述 Farmer John wants to connect his N (1 <= N ...

  7. [USACO 2017 Dec Gold] Tutorial

    Link: USACO 2017 Dec Gold 传送门 A: 为了保证复杂度明显是从终结点往回退 结果一开始全在想优化建边$dfs$……其实可以不用建边直接$multiset$找可行边跑$bfs$ ...

  8. NC24083 [USACO 2017 Dec P]Greedy Gift Takers

    NC24083 [USACO 2017 Dec P]Greedy Gift Takers 题目 题目描述 Farmer John's nemesis, Farmer Nhoj, has N cows ...

  9. USACO 2016 February Contest, Gold解题报告

    1.Circular Barn   http://www.usaco.org/index.php?page=viewproblem2&cpid=621 贪心 #include <cstd ...

随机推荐

  1. 201621123060《JAVA程序设计》第十三周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 为你的系统增加网络功能(购物车.图书馆管理.斗地主等)-分组完成 为了让你的系统可以被多个用户通过网 ...

  2. numpy.random.seed()方法

    先贴参考链接: https://stackoverflow.com/questions/21494489/what-does-numpy-random-seed0-do numpy.random.se ...

  3. Alpha冲刺第十二天

    Alpha冲刺第十二天 站立式会议 项目进展 项目核心功能,如学生基本信息管理模块,学生信用信息模块,奖惩事务管理模块等等都已完成,测试工作大体结束. 问题困难 项目结束后对项目的阶段性总结缺乏一定的 ...

  4. Python处理图片缩略图

    CPU 密集型任务和 IO 密集型任务分别选择多进程multiprocessing.Pool.map 和多线程库multiprocessing.dummy.Pool.map import os imp ...

  5. Beta冲刺Day3

    项目进展 李明皇 今天解决的进度 完善了程序的运行逻辑(消息提示框等) 明天安排 前后端联动调试 林翔 今天解决的进度 向微信官方申请登录验证session以维护登录态 明天安排 继续完成维护登录态 ...

  6. Java面试题合集(二)

    接下来几篇文章准备系统整理一下有关Java的面试题,分为基础篇,javaweb篇,框架篇,数据库篇,多线程篇,并发篇,算法篇等等,陆续更新中.其他方面如前端后端等等的面试题也在整理中,都会有的. 注: ...

  7. JAVA_SE基础——42.final修饰符

    高手勿喷~ final关键字可用于修饰类.变量和方法,它有"这是无法改变的"或者"最终"的含义,因此被final修饰的类.变量和方法将具有以下特征: 1.fin ...

  8. python中 return 的用法

    return 语句就是讲结果返回到调用的地方,并把程序的控制权一起返回 程序运行到所遇到的第一个return即返回(退出def块),不会再运行第二个return. 要返回两个数值,写成一行即可: de ...

  9. python入门(12)dict

    python入门(12)dict Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度. 举个例 ...

  10. API验证及AES加密

    API验证 API验证: a. 发令牌: 静态 PS: 隐患 key被别人获取 b. 动态令牌 PS: (问题越严重)用户生成的每个令牌被黑客获取到,都会破解 c. 高级版本 PS: 黑客网速快,会窃 ...