2020-01-11 20:53阅读: 748评论: 0推荐: 3

Codeforces Round #613 (Div. 2)

A. Mezo Playing Zoma (CF 1285 A)

题目大意

机器人初始位置为0,给了一串指令告诉机器人是往左走一格还是右走一格,部分指令可能会丢失,问机器人最终可能的位置的情况数。

解题思路

L,R的个数加一即为答案。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
int main(void) {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
int n;
string s;
cin>>n>>s;
int l=0,r=0;
for(int i=0;i<n;++i){
if (s[i]=='L') ++l;
else ++r;
}
int ans=l+r+1;
cout<<ans<<endl;
}


B. Just Eat It! (CF 1285 B)

题目大意

给定一个数列,问整个区间和以及部分区间和哪个大。

解题思路

区间和最值的用dynamic programming,特判最值是否在全部区间取的即可。dp[i]表示前i个数,其中第i个数必取的最大值。则dp[i]=max(dp[i1],a[i]),答案为max1in(dp[i])

对应为代码注释部分。

注意到区间可以理解为是去掉从开头的连续几个数以及从结尾的连续几个数。去掉后如果和会变大或者不变,则说明存在某点的前缀和或后缀和小于等于0,如此两边扫一遍也行。

神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;
template <typename T>
void read(T &x) {
int s = 0, c = getchar();
x = 0;
while (isspace(c)) c = getchar();
if (c == 45) s = 1, c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s) x = -x;
}
template <typename T>
void write(T x, char c = ' ') {
int b[40], l = 0;
if (x < 0) putchar(45), x = -x;
while (x > 0) b[l++] = x % 10, x /= 10;
if (!l) putchar(48);
while (l) putchar(b[--l] | 48);
putchar(c);
}
const int N=1e5+8;
int n;
LL a[N];
bool check(){
LL sum=0;
for(int i=0;i<n;++i){
sum+=a[i];
if (sum<=0) return 0;
}
sum=0;
for(int i=n-1;i>=0;--i){
sum+=a[i];
if (sum<=0) return 0;
}
return 1;
}
int main(void) {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
int kase; read(kase);
for (int i = 1; i <= kase; i++) {
//printf("Case #%d: ", i);
read(n);
for(int i=0;i<n;++i) read(a[i]);
if (check()) puts("YES");
else puts("NO");
}
return 0;
}
/* int main(void) {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
int kase; read(kase);
for (int i = 1; i <= kase; i++) {
//printf("Case #%d: ", i);
int n;
LL sum=0;
read(n);
LL a[n+5]={0};
for(int i=0;i<n;++i){
read(a[i]);
sum+=a[i];
}
LL ans=a[0];
LL dp[n+5]={0};
dp[0]=a[0];
bool qwq=true;
for(int i=1;i<n;++i){
if (dp[i-1]+a[i]>a[i]) dp[i]=dp[i-1]+a[i];
else {dp[i]=a[i]; qwq=false;}
ans=MAX(ans,dp[i]);
}
if (qwq) for(int i=0;i<n-1;++i) if (dp[i]==sum) qwq=false;
if (dp[n-1]==sum&&qwq&&ans==sum) ans=sum-1;
if (ans>=sum) printf("NO\n");
else printf("YES\n");
}
return 0;
} */


C. Fadi and LCM (CF 1285 C)

题目大意

给定x,找出一对a,b使得lcm(a,b)=xmax(a,b)最小。

解题思路

lcm(a,b)=a×bgcd(a,b)=x,令a=k1×gcd(a,b),b=k2×gcd(a,b),则k1×k2×gcd(a,b)=x,不失一般性,我们设abk1k2,我们要最小化b,即最小化k2×gcd(a,b),即最大化k1,即找到最大的k1k1k2,由于x1012,而k1x,所以我们从大到小枚举k1判断lcm(a,b)是否等于x即可。我们也可以假设gcd(a,b)=1,这是始终有解且一定是最小的。

神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;
template <typename T>
void read(T &x) {
int s = 0, c = getchar();
x = 0;
while (isspace(c)) c = getchar();
if (c == 45) s = 1, c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s) x = -x;
}
template <typename T>
void write(T x, char c = ' ') {
int b[40], l = 0;
if (x < 0) putchar(45), x = -x;
while (x > 0) b[l++] = x % 10, x /= 10;
if (!l) putchar(48);
while (l) putchar(b[--l] | 48);
putchar(c);
}
int main(void) {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
LL x=0;
read(x);
LL qwq=sqrt(x);
while(qwq){
if (x%qwq==0) if(__gcd(qwq,x/qwq)==1) break;
--qwq;
}
printf("%lld %lld\n",qwq,x/qwq);
return 0;
}


D. Dr. Evil Underscores (CF 1285 D)

题目大意

给定n个数ai (i=1,2,3,...,n),要求找一个数X,最小化max1in(aiX)。输出这个最小值。

解题思路

异或题盲猜trie
然后弄dp然后暴毙
经过分析我们发现,对于在二进制下的某一位,如果全都是1或者0,我们都可以对X在这一位添10来变小,但如果某一位既有1又有0,那这一位将不可避免的存在1。那么我们从高位起,判断该位,如果全部是01,那么答案在该位就是0,否则将是1,然后分X在该位是01两种情况继续搜下去即可。在trie树上跑。

神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;
template <typename T>
void read(T &x) {
int s = 0, c = getchar();
x = 0;
while (isspace(c)) c = getchar();
if (c == 45) s = 1, c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s) x = -x;
}
template <typename T>
void write(T x, char c = ' ') {
int b[40], l = 0;
if (x < 0) putchar(45), x = -x;
while (x > 0) b[l++] = x % 10, x /= 10;
if (!l) putchar(48);
while (l) putchar(b[--l] | 48);
putchar(c);
}
const int N=4e6+7;
int trie[N][2],n,m=1;
void add(int x){
int t=1;
for(int i=29;i>=0;--i){
if (!trie[t][(x>>i)&1]) trie[t][(x>>i)&1]=++m;
t=trie[t][(x>>i)&1];
}
}
int DFS(int t,int len){
if (len<0) return 0;
if (!trie[t][0]) return DFS(trie[t][1],len-1);
if (!trie[t][1]) return DFS(trie[t][0],len-1);
return (min(DFS(trie[t][0],len-1),DFS(trie[t][1],len-1))|(1<<len));
}
int main(void) {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
read(n);
for(int u,i=1;i<=n;++i){
read(u);
add(u);
}
printf("%d\n",DFS(1,29));
return 0;
}


E. Delete a Segment (CF 1285 E)

题目大意

给定n条线段,线段之间若有交叉(端点值相同也算交叉)或者重合则可以联合起来成为新的一条(可能)更长的线段,现在要求去掉一条线段(不能不去),使得去掉后联合后的线段最多,问是多少。

解题思路

我们考虑去掉一条线段后能够增加多少个线段。

我们可以发现,如果该线段所覆盖的区间里,存在某个子区间,它只被该线段覆盖的话,去掉该线段后,这里就有一个空隙。而如果有x个空隙出来的话,那就会多出x个线段出来。这启示我们从空隙数量的角度来解决联合线段的个数。

我们考虑如何求只被该线段覆盖的区间的数量。

我们按左端点从小到大排列枚举,对于当前枚举的这条线段i,前i1条线段的右端点的最大值为r,次大值为r0,则对于其右端点为r的某条线段j来说,它所覆盖的[lj,rj]区间中,(r0,rj]区间(或[lj,rj],如果lj>r0)并没有被其他线段覆盖,如果第i条线段的左端点li>r0的话,那么区间(r0,li)这部分区间就只被线段j覆盖,然后我们更新rr0重复操作,得到每条线段覆盖的区间内没被其他线段覆盖的区间数cnti(或者说线段内的空隙),还有本身存在的空隙gap,最后由植树原理得答案ans=gap+max1in(cnti)+1

要特判全部线段没有交叉的情况,此时最终答案应是ans=n1

神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;
template <typename T>
void read(T &x) {
int s = 0, c = getchar();
x = 0;
while (isspace(c)) c = getchar();
if (c == 45) s = 1, c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s) x = -x;
}
template <typename T>
void write(T x, char c = ' ') {
int b[40], l = 0;
if (x < 0) putchar(45), x = -x;
while (x > 0) b[l++] = x % 10, x /= 10;
if (!l) putchar(48);
while (l) putchar(b[--l] | 48);
putchar(c);
}
int main(void) {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
int kase; read(kase);
for (int i = 1; i <= kase; i++) {
//printf("Case #%d: ", i);
int n;
vector<pair<int,int>> a;
read(n);
for(int l,r,i=0;i<n;++i){
read(l);
read(r);
a.push_back(make_pair(l,r));
}
sort(a.begin(),a.end(),less<pair<int,int>>());
int r=a[0].second,ans=0,ma=-2147483644,cnt=0,gap=0;
for(int i=1;i<n;++i){
if (a[i].first>r){
++gap;
r=a[i].second;
ma=-2147483644;
ans=max(ans,cnt);
cnt=0;
}else if (ma==-2147483644){
ma=min(r,a[i].second);
r=max(r,a[i].second);
}else{
if (a[i].first>ma) ++cnt;
if (a[i].second>=r){
ans=max(ans,cnt);
cnt=0;
}
ma=max(ma,min(r,a[i].second));
r=max(r,a[i].second);
}
}
ans=max(ans,cnt);
ans=ans+gap+1;
if (gap+1==n) ans=n-1;
printf("%d\n",ans);
}
return 0;
}


F. Classical? (CF 1285 F)

题目大意

给定n个数ai,求max1i<jnlcm(ai,aj)

解题思路

lcm(ai,aj)=ai×ajgcd(ai,aj)。我们可以枚举gcd(ai,aj),然后所有数都除以gcd(ai,aj),这样我们要寻找的就是两个互质的数ax,ay,它们的乘积最大。我们从大到小遍历这些数,对于当前的数ai,我们想知道大于它的数中是否有与它互质的,即求aj>ai[gcd(ai,aj)=1]是否大于零。这是个经典式子,由于[gcd(ai,aj)=1]=d|gcd(ai,aj)μ(d),我们对其变形:

aj>ai[gcd(ai,aj)=1]=aj>aid|gcd(ai,aj)μ(d)

我们改变它的求和顺序,由于d肯定是ai的因子,我们枚举它的每一个因子,这样的因子在求和式中出现次数为cntd,得

aj>aid|gcd(ai,aj)μ(d)=d|aiμ(d)cntd

其中cntd表示大于ai的数中是d的倍数的个数,μ(x)是莫比乌斯函数。

这样我们维护一个cnt数组,就能够知道是否有与ai互质的数,拿一个指针往回扫,扫到那个互质的数,然后计算答案aiajgcd(ai,aj)(因为这里的两个数ai,aj都除以了一次gcd(ai,aj))。注意下次再遇到有互质的时候,指针从上次的位置继续往回找即可,不需要重新回到原来位置往回扫,因为我们是从大到小遍历这些数,小的数与前面的数的乘积不可能大于原先的答案,这里可以用栈处理。

时间复杂度为O(i=1nσ0(i)2),其中σ0(n)=d|n1

还可以再优化一下,我们把ai×ajgcd(ai,aj)视为ai×ajgcd(ai,aj),于是把aj的每个因子都储存下来,这样虽然会增加原本不应有的答案,但这些答案会小于aj对应的答案,对我们要找的最大值无影响。

也就是说我们对于每个ai,把它的因子dij全部加到数组里,因为拿ai与另一个数求LCM不会小于拿它的某个因子di与另一个数求LCM,所以这对最终答案没有影响,尽管会增加原本不会出现的答案。然后再从大到小枚举ai,寻找比ai大的且与ai互质的数,这仍是上面的方法,只是省去了枚举gcd(ai,aj)的操作,最终的时间复杂度为(i=1nσ0(i))

神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
template <typename T>
void read(T &x) {
int s = 0, c = getchar();
x = 0;
while (isspace(c)) c = getchar();
if (c == 45) s = 1, c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s) x = -x;
}
template <typename T>
void write(T x, char c = ' ') {
int b[40], l = 0;
if (x < 0) putchar(45), x = -x;
while (x > 0) b[l++] = x % 10, x /= 10;
if (!l) putchar(48);
while (l) putchar(b[--l] | 48);
putchar(c);
}
const int N=1e5+8;
bool sign[N];
int n,ma,u[N],cnt[N];
LL ans;
vector<int> d[N];
stack<LL> s;
void pre(){
for(int i=1;i<=ma;++i){
for(int j=i;j<=ma;j+=i) d[j].push_back(i);
if (i==1) u[i]=1;
else if (i/d[i][1]%d[i][1]==0) u[i]=0;
else u[i]=-u[i/d[i][1]];
}
}
int coprime(int x){
int tmp=0;
for(int i:d[x])
tmp+=u[i]*cnt[i];
return tmp;
}
void updata(int x,int val){
for(int i:d[x])
cnt[i]+=val;
}
int main(void) {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
read(n);
for(int a,i=1;i<=n;++i){
read(a);
sign[a]=true;
ma=MAX(ma,a);
}
pre();
for(int i=1;i<=ma;++i)
for(int j=2;i*j<=ma;++j)
sign[i]|=sign[i*j];
for(int qwq,i=ma;i>=1;--i){
if (!sign[i]) continue;
qwq=coprime(i);
while(qwq){
if (__gcd(s.top(),(LL)i)==1){
ans=max(s.top()*(LL)i,ans);
--qwq;
}
updata(s.top(),-1);
s.pop();
}
updata(i,1);
s.push((LL)i);
}
write(ans,'\n');
return 0;
}


本文作者:~Lanly~

本文链接:https://www.cnblogs.com/Lanly/p/12181097.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   ~Lanly~  阅读(748)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.