[题解] Atcoder Regular Contest ARC 147 A B C D E 题解
A - Max Mod Min
非常诈骗。一开始以为要观察什么神奇的性质,后来发现直接模拟就行了。可以证明总操作次数是\(O(nlog a_i)\)的。具体就是,每次操作都会有一个数a被b取模,然后变成a%b。注意到a%b是\(\leq \frac a2\)的,并且a被操作之后会变成整个数据最小的数,作为下一轮的b。所以把原数组排序后,最小值的位置是不断往左移的,每次移动1个位置,直接模拟即可。
时间复杂度\(O(nlog a_i)\)。
点击查看代码
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define pb push_back
#define fi first
#define se second
#define mpr make_pair
using namespace std;
int n,a[200010];
multiset <int> st;
int main()
{
cin>>n;
rep(i,n) scanf("%d",&a[i]),st.insert(-a[i]);
int ans=0;
while(st.size()>1)
{
int val=-*st.begin(),dv=-*st.rbegin();
st.erase(st.begin());
val%=dv;
if(val>0) st.insert(-val);
++ans;
}
cout<<ans<<endl;
return 0;
}
B - Swap to Sort
把数值分成2类,奇数应在奇数位置,偶数应在偶数位置。但是初始会有一些奇数在偶数位置,也会有一些偶数在奇数位置。容易发现这两类数的个数是相等的,令其中一类的个数为k。A类操作的作用就是把一个奇数位置的偶数和一个偶数位置的奇数交换。发现只要k次A操作就可以让所有奇数在奇数位置,偶数在偶数位置,完成这一步后对所有奇数位置/所有偶数位置分别用B操作冒泡排序即可,一共40000次操作左右;且这k次A操作不可缺少。其他工作都可以用B操作完成。让所有奇数在奇数位置,偶数在偶数位置的具体做法是:先随便找一个在偶数位置的奇数,再随便找一个奇数位置的偶数,用B操作把它们换到相邻,然后一次A即可,一共40000次左右。这样总操作数不会超过100000。
点击查看代码
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define pb push_back
#define fi first
#define se second
#define mpr make_pair
using namespace std;
int n,p[410];
vector <pii> ans;
void swpA(int i)
{
swap(p[i],p[i+1]);
ans.pb(mpr(0,i));
}
void swpB(int i)
{
swap(p[i],p[i+2]);
ans.pb(mpr(1,i));
}
void isort(int w)
{
vector <int> poss;
repn(i,n) if(i%2==w) poss.pb(i);
while(true)
{
bool hv=false;
rep(i,poss.size()-1) if(p[poss[i]]>p[poss[i+1]])
{
swpB(poss[i]);
hv=true;break;
}
if(!hv) break;
}
}
int main()
{
cin>>n;
repn(i,n) cin>>p[i];
while(true)
{
bool hv=false;
repn(i,n) if(i%2==0&&p[i]%2==1)
{
int to=-1;
repn(j,n) if(j%2==1&&p[j]%2==0) to=j;
if(to>i)
{
int stp=(to-i)/2,cur=i;
rep(j,stp)
{
swpB(cur);
cur+=2;
}
swpA(cur);
}
else
{
int stp=(i-to)/2,cur=i;
rep(j,stp)
{
swpB(cur-2);
cur-=2;
}
swpA(cur-1);
}
hv=true;break;
}
if(!hv) break;
}
isort(0);isort(1);
cout<<ans.size()<<endl;
rep(i,ans.size())
{
if(ans[i].fi==0) printf("A ");
else printf("B ");
printf("%d\n",ans[i].se);
}
return 0;
}
C - Min Diff Sum
考虑最终选出的所有位置,从小到大排序,令第\(\lfloor \frac n2 \rfloor\)个为x。则排序时排在x左边,且其位置的值\(<x\)的变量如果能往右移哪怕1个位置,也是好的,但是为什么不能移,因为它已经在对应线段的右端点了。排序时排在x右边的同理。这启发我们可以枚举所在的区间。把所有\(l_i\)和\(r_i\)排序并去重,枚举排好的序列中相邻的2个位置\(a,b\),计算\(a \leq x \leq b\)时的最优答案。找出所有\(r_i < a\)的线段,它们对应的值只能选\(r_i\),令这样的线段有ca个。所有\(l_i > b\)的只能选\(l_i\),令这样的线段有cb个。其他的显然是选与x相同最好。如果\(ca \geq cb\),显然x选a比较好,否则x选b比较好。记录几个部分的线段数量、端点位置之和等信息即可\(O(1)\)计算。
时间复杂度\(O(nlogn)\)。
点击查看代码
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <LL,LL>
#define pb push_back
#define fi first
#define se second
#define mpr make_pair
using namespace std;
LL n,l[300010],r[300010],lsum=0,rsum=0,lcnt=0,rcnt=0,stat[300010],ans=LONG_LONG_MAX;
vector <pair <LL,pii> > op;
vector <LL> L;
int main()
{
cin>>n;
LL addi=0;
rep(i,n)
{
scanf("%lld%lld",&l[i],&r[i]);
op.pb(mpr(l[i],mpr(-1,i)));op.pb(mpr(r[i],mpr(1,i)));
stat[i]=1;
L.pb(l[i]);
}
sort(L.begin(),L.end());
rep(i,L.size())
{
addi+=L[i]*rcnt-rsum;
rsum+=L[i];++rcnt;
}
sort(op.begin(),op.end());
rep(i,op.size()-1)
{
if(op[i].se.fi==-1)
{
stat[op[i].se.se]=0;
--rcnt;rsum-=l[op[i].se.se];
addi-=rsum-l[op[i].se.se]*rcnt;
}
else
{
stat[op[i].se.se]=1;
addi+=r[op[i].se.se]*lcnt-lsum;
++lcnt;lsum+=r[op[i].se.se];
}
LL lb=op[i].fi,ub=op[i+1].fi,mid=n-lcnt-rcnt,act=(lcnt>=rcnt ? lb:ub);
LL res=0;
res+=act*(mid+rcnt)*lcnt-lsum*(mid+rcnt);
res+=rsum*(mid+lcnt)-act*(mid+lcnt)*rcnt;
ans=min(ans,res+addi);
//cout<<lb<<' '<<ub<<' '<<lcnt<<' '<<rcnt<<' '<<res<<' '<<lsum<<' '<<rsum<<' '<<act<<endl;
}
cout<<ans<<endl;
return 0;
}
D - Sets Scores
出这种代码只有1行的数学诈骗题有意思吗.jpg
atcoder出题真是越来越魔怔了,arc出屑题,abc出大量板子题
n个集合,会有n+1个空隙(包括头尾)。如果第i和第i+1个集合中有一个不同的元素x,就在它们两个集合之间的空隙中填上一个编号为x的标记。同时,把1号集合中含有的元素对应的标记都填到它前面的空隙中。n号集合同理,填到它后面的空隙中。注意到每种标记都有偶数个。题目中的要求等价于每两个相邻集合中间的空隙用有恰好1个标记,首尾空隙任意。题目要求的是\(\sum\prod_i i在所有集合中出现的次数总和\),它等价于我们枚举m个集合\(t_1 \cdots t_m\),表示要求\(i在t_i\)中出现,求出满足这个条件的填标记方案数,并对所有枚举方式求和。对于某种填标记方案,i在\(t_i\)中出现当且仅当\(t_i\)前面有奇数个i号标记。乍一看很难判断,但是其实我们只要确定哪些中间空隙(不包括首尾)含有i号标记,再确定\(t_i\),首尾空隙中是否有i号标记就已经确定了,因为别忘了每种标记都有偶数个。所以可以先枚举所有\(t_i\),方案数\(n^m\);再枚举每个中间空隙填了哪种类型的标记,方案数\(m^{n-1}\),两个乘起来即可。
时间复杂度\(O(logn+logm)\)。
点击查看代码
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define pb push_back
#define fi first
#define se second
#define mpr make_pair
using namespace std;
const LL MOD=998244353;
LL qpow(LL x,LL a)
{
LL res=x,ret=1;
while(a>0)
{
if((a&1)==1) ret=ret*res%MOD;
a>>=1;
res=res*res%MOD;
}
return ret;
}
LL n,m;
int main()
{
cin>>n>>m;
LL ans=qpow(m,n-1);
(ans*=qpow(n,m))%=MOD;
cout<<ans<<endl;
return 0;
}
E - Examination
还是挺妙的,场上有100多个人切了,后来果然发现有原题
判无解的方法大家肯定都会了,把a和b分别从小到大排序,如果有一位\(a_i < b_i\)就无解。
有解的情况,我们考虑找出一个集合,它里面的元素可能会互相交换分数;剩下的元素分数都不变,自给自足,并给答案贡献1,最优解显然可以通过找出一个最小的合法集合来确定。
显然,所有\(a_i < b_i\)的人都在这个集合里;所有\(a_i=b_i\)的人都不在这个集合里,因为它们如果在集合里,最低要求就是他自己本身已有的分数,什么贡献都不能有,还可能会侵占资源。其他\(a_i > b_i\)的,可以考虑把它们一个一个加入集合中,使得集合合法;同时也要使加入的元素尽量少。分析一下集合s合法的条件,其实是:对于任意i,满足\(\sum_j^{|s|} [b_j<i]-[a_j<i]\)。证明也比较显然,这样每一个\(a_i\)都能在他前面找到一个\(b_i\)与其匹配;不满足这个条件则不行。一开始集合中有所有\(a_i<b_i\)的人,考虑贪心向其中添加一些元素,使得其合法,且添加的元素最少。我们每次找到最小的i使得i不满足上面那个式子的条件。显然,我们需要一个\(b_j \leq i,a_j > i\)的元素来救。发现\(b_i\)的大小是无关紧要的,因为i之前的位置都已经满足上面式子的条件。所以我们肯定应该选择\(a_j\)最大的j添加。实现的话,从小到大把所有i扫描一遍即可,用一个优先队列维护可以加入集合的元素。代码很好写,具体实现可以看代码。
时间复杂度:\(O(nlogn)\)。
点击查看代码
#include <bits/stdc++.h>
#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <int,int>
#define pb push_back
#define fi first
#define se second
#define mpr make_pair
using namespace std;
int n,a[300010],b[300010],mp[600010],ans=0;
vector <int> as,bs,dsc,add[600010];
priority_queue <int> q;
int main()
{
cin>>n;
rep(i,n)
{
scanf("%d%d",&a[i],&b[i]);
as.pb(a[i]);bs.pb(b[i]);
dsc.pb(a[i]);dsc.pb(b[i]);
}
sort(as.begin(),as.end());sort(bs.begin(),bs.end());
rep(i,n)
{
if(as[i]<bs[i])
{
puts("-1");
return 0;
}
}
sort(dsc.begin(),dsc.end());dsc.erase(unique(dsc.begin(),dsc.end()),dsc.end());
rep(i,n)
{
a[i]=lower_bound(dsc.begin(),dsc.end(),a[i])-dsc.begin();
b[i]=lower_bound(dsc.begin(),dsc.end(),b[i])-dsc.begin();
if(a[i]<b[i]) --mp[a[i]],++mp[b[i]];
else if(a[i]>b[i]) add[b[i]].pb(i);
else ++ans;
}
int sum=0;
rep(i,dsc.size())
{
sum+=mp[i];
rep(j,add[i].size()) q.push(a[add[i][j]]);
while(sum<0)
{
int f=q.top();q.pop();
++sum;--mp[f];
}
}
ans+=q.size();
cout<<ans<<endl;
return 0;
}
[题解] Atcoder Regular Contest ARC 147 A B C D E 题解的更多相关文章
- [题解] Atcoder Regular Contest ARC 151 A B C D E 题解
点我看题 昨天刚打的ARC,题目质量还是不错的. A - Equal Hamming Distances 对于一个位置i,如果\(S_i=T_i\),那么不管\(U\)的这个位置填什么,对到\(S\) ...
- [题解] Atcoder Regular Contest ARC 148 A B C E 题解
点我看题 题目质量一言难尽(至少对我来说 所以我不写D的题解了 A - mod M 发现如果把M选成2,就可以把答案压到至多2.所以答案只能是1或2,只要判断答案能不能是1即可.如果答案是1,那么M必 ...
- [题解] Atcoder Regular Contest ARC 146 A B C D 题解
点我看题 A - Three Cards 先把所有数按位数从多到少排序,答案的位数一定等于位数最多的三个数的位数之和\(tot\).对于每个i,把有i位的数排序,并记录每个i的排序结果.最后枚举答案中 ...
- AtCoder Regular Contest 094 (ARC094) CDE题解
原文链接http://www.cnblogs.com/zhouzhendong/p/8735114.html $AtCoder\ Regular\ Contest\ 094(ARC094)\ CDE$ ...
- AtCoder Regular Contest 096
AtCoder Regular Contest 096 C - Many Medians 题意: 有A,B两种匹萨和三种购买方案,买一个A,买一个B,买半个A和半个B,花费分别为a,b,c. 求买X个 ...
- AtCoder Regular Contest 061
AtCoder Regular Contest 061 C.Many Formulas 题意 给长度不超过\(10\)且由\(0\)到\(9\)数字组成的串S. 可以在两数字间放\(+\)号. 求所有 ...
- AtCoder Regular Contest 092
AtCoder Regular Contest 092 C - 2D Plane 2N Points 题意: 二维平面上给了\(2N\)个点,其中\(N\)个是\(A\)类点,\(N\)个是\(B\) ...
- AtCoder Regular Contest 093
AtCoder Regular Contest 093 C - Traveling Plan 题意: 给定n个点,求出删去i号点时,按顺序从起点到一号点走到n号点最后回到起点所走的路程是多少. \(n ...
- AtCoder Regular Contest 094
AtCoder Regular Contest 094 C - Same Integers 题意: 给定\(a,b,c\)三个数,可以进行两个操作:1.把一个数+2:2.把任意两个数+1.求最少需要几 ...
随机推荐
- stringstrean类中关于clear和str的比较
stringstream类涉及到多次类型转换的时候容易出现异常错误 因为第一次数据如果读入eof或者输出完整来到eof,此时stringstream会自动为其添上eofbit标志位,此时继续进行任何操 ...
- html元素height(width)是怎么确定的?
1.若height是确定的(比如height:100px),则height直接可确定(还受min-height,max-height影响,见height,min-height,max-heigth的作 ...
- 闭包类型(Fn,FnMut,FnOnce)和move关键字
move关键字是强制让环境变量的所有权转移到闭包中而不管是不是发生了所有权的转移 move关键字和匿名函数是否是FnOnce没有必然联系,之和匿名函数体有关 当匿名函数体里转移了环境变量的所有权的时候 ...
- openstack 虚拟机网卡被重名为cirename0
虚拟机网卡被重名为cirename0 在虚拟机挂载多网卡情况下,你在虚拟机上卸载网卡后,再创建新的port挂给虚拟机使用,如果虚拟机不经过重启的话,是不会有任何问题的.但是,如果虚拟机重启了,你 ...
- Java8新特性: CompletableFuture详解
CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步回调.流式处理.多个Future组合处理的能力,使Java在处理多任务的 ...
- 配置 Druid 数据源及密码加密-SpringBoot 2.7 实战基础
在SpringBoot中配置 Druid 数据源及密码加密的方法 前文集成 MyBatis Plus,实现了一组增删改查接口.在启动服务时,从控制台中可以看出 Spring Boot 默认使用 Hik ...
- 万答#2,一样的Python代码,为什么可以删表,却不能更新数据
欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 问题 运行下面的这段Python代码,却总是无法更新数据: import pym ...
- 处理化学SDF文件出现乱码的解决经验
近期,在VS2019中用WTL编写一个处理化学SDF文件的程序,遇到多处数据出现乱码的问题,典型一处情况如下:在原始SDF文件的一个字段中,有个形如下面的文字信息: https://product.p ...
- Min GW 安装教程(转载)
下载方式一:1.百度搜索关键词"mingw",点击第一个:2.进入官网后,点击右上角的"Downloads":3.进入第三个页面后,点击"mingw- ...
- RS485自动收发切换电路 [原创www.cnblogs.com/helesheng]
RS485是最常见的一种远距离可靠传输和组网的UART串口信号接口协议.与同样传输UART串口信号的RS422协议相比,RS485使用半双工通信,即只有一个信道,在同一时刻要么从A到B,要么从B到A传 ...