首页 > 其他分享 >Atcoder Beginner Contest 367

Atcoder Beginner Contest 367

时间:2024-08-18 08:54:01浏览次数:9  
标签:Atcoder le 数组 Beginner 200001 int 哈希 367 dis

A.Shout Everyday \(\text{Diff }43\)

给你 \(24\) 小时制下的 \(A,B,C\) 三个时刻,问 \(A\) 是否在 \([B,C]\) 范围内

考虑到先将 \(B,C\) 加上一个 \(24\),假如 \(C\) 比 \(B\) 小,将 \(C\) 再加上一个 \(24\),这样可以保证严格的 \(A\lt B,C\),此时直接判断是否存在一个 \(k\),使得 \(A+24k\in[B,C]\) 即可

#include<bits/stdc++.h>
using namespace std;
int main(){
	int a,b,c;
	cin>>a>>b>>c;
	if(c<b) c+=24;
	while(a<=c){
//		cout<<a<<" in "<<b<<" "<<c<<endl;
		if(a>=b and a<=c){
			cout<<"No"<<endl;
			return 0;
		}
		a+=24;
	}
	cout<<"Yes"<<endl;
}

B.Cut .0 \(\text{Diff }43\)

给定一个小数,去除多余的后导零或小数点

比第一题水,不说了

#include<bits/stdc++.h>
using namespace std;
int main(){
	string x;
	cin>>x;
	for(int i=x.length()-1;i>=0;--i){
		if(x[i]=='.'){
			x.pop_back();
			break;
		}
		if(x[i]!='0') break;
		x.pop_back();
	}
	cout<<x<<endl;
}

Enumerate Sequences \(\text{Diff }234\)

按字典序输出所有满足条件的,长度为 \(N\) 的序列 \(A\)

  • \(\forall A_{i}\in[1,R_{i}]\)
  • \(\sum^{N}_{i=1}A_{i}\mod K=0\)

\(N\le 8,K\le 10,R_{i}\le 5\)

显然应该是爆搜

#include<bits/stdc++.h>
using namespace std;
int a[9];
int n,k;
int r[9];
void dfs(int now,int nowsum){
	if(now>n){
		if(nowsum%k==0){
			for(int i=1;i<=n;++i){
				cout<<a[i]<<" ";
			}
			cout<<endl;
		}
		return;
	}
	for(int i=1;i<=r[now];++i){
		a[now]=i;
		dfs(now+1,nowsum+i);
	}
}
int main(){
	cin>>n>>k;
	for(int i=1;i<=n;++i){
		cin>>r[i];
	}
	dfs(1,0);
}

D.Pedometer \(\text{Diff }1037\)

顺时针给你一个环,告诉你环上各边的权值

定义 \(dis(x,y)\) 为从 \(x\) 顺时针走到 \(y\) 的路径长

请你求出所有满足 \(dis(u,v)\mod M=0\) 的点对数量

\(N\le 2\times 10^{5},M\le 10^{6}\)

显然不能每次都改变整个 \(dis\) 数组的值

首先先来模拟一下,先考虑暴力,每次枚举出发点,开一个数组 \(dis\) 用来表示从出发点到各点的距离,观察当出发点从 \(i\) 变成 \(i+1\) 的时候,\(dis\) 数组会如何变化

2 1 4 3

i   dis
1   0 2 3 7
2   8 0 1 5
3   7 9 0 4

可以发现,每当我们将 \(i\) 向后移了一位,其实就相当于以下两步操作:

  • 将 \(dis_{i}\) 加上总路径和(其实就是绕了一圈)
  • 将整个 \(dis\) 数组减去 \(dis_{i+1}\)

发现第二步实际上是可以优化的,因为这道题是取模操作,方便我们优化. 注意到若 \(x\mod M=0\),则应该有 \((x-k)\mod M=M-(k\mod M)\),因此我们可以考虑直接根据这样的性质来转移余数而不是整个 \(dis\) 数组,这样这个题就做完了

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,sum;
int a[200001],r[1000001],pres[200002],opres[200002];
signed main(){
	scanf("%lld %lld",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%lld",&a[i]);
		sum+=a[i];
	}
	for(int i=1;i<=n-1;++i){
		pres[i]=pres[i-1]+a[i];
		opres[i]=pres[i];
		r[pres[i]%m]++;
	}
	r[0]++;
	int ans=0,rm=m,msum=0;
	for(int i=1;i<=n;++i){
		m+=opres[i]-msum;
		msum+=pres[i]-msum;
		r[pres[i==1?n:i-1]%rm]--;
		pres[i==1?n:i-1]+=sum;
		r[pres[i==1?n:i-1]%rm]++;
		ans+=r[msum%rm];
	}
	cout<<ans-n<<endl;
}

E.Permute K times \(\text{Diff }1370\)

给定长度为 \(N\) 的序列 \(X,A\),其中 \(\forall x_{i}\in[1,N]\),对序列 \(A\) 执行 \(K\) 次下列操作:

  • 同时将全部 \(A_{i}\) 赋值为 \(A_{X_{i}}\)

求最终的序列 \(A\)

\(N\le 2\times 10^{5},K\le 10^{18}\)

好题。

この問題は、 ダブリング と呼ばれるテクニックで解くことができます

我并不知道 ダブリング 是啥算法,但是我猜大抵是倍增罢

注意到我们每次在 \(A\) 数组上操作其实就相当于在 \(X\) 数组上操作,因此我们暂时抛开 \(A\) 数组不管

又注意到 \(X\) 数组上的转移是有传递性的,比如两次操作后的 \(X'_{i}=X_{X_{i}}\)

定义 \(p_{i,j}\) 表示操作 \(2^{i}\) 次以后 \(X_{j}\) 的值,我们姑且先不考虑 \(K\) 的限制,转移是显而易见的

\[p_{i,j}=p_{i-1,p_{i-1,j}} \]

初始化 \(p_{0,i}=x_{i}\)

但是显然有了这个东西还不够,因为它让我们求恰好 \(K\) 次操作时候的 \(K\) 数组值,而我们现在只有 \(2^{i}\) 的数组值.

写到这基本就能想到了,搞一个二进制分解就行了

其实本来想暴力建图弄一个内向基环树的,因为在树上直接维护环信息,到最后直接取模 \(O(1)\) 就出了. 这个思路是假的,但是如果 \(K\ge N\),这个思路就是对的了. 瓶颈在这个算法只能求出走进环里的节点的末态,而对于很长的链而 \(K\) 非常小的时候,存在节点无法走入环内,由于无法维护环外信息,因此复杂度会被卡成 \(n^{2}\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k;
int p[61][200001],a[200001],x[200001];
signed main(){	
	cin>>n>>k;
    for(int i=1;i<=n;++i){
        cin>>p[0][i];
    }
    for(int i=1;i<=n;++i){
        cin>>a[i];
    }
    for(int i=1;i<=60;++i){
        for(int j=1;j<=n;++j){
            p[i][j]=p[i-1][p[i-1][j]];
        }
    }
    for(int i=1;i<=n;++i){
        x[i]=i;
    }
    for(int i=0;i<=60;++i){
        if(k&1){
            for(int j=1;j<=n;++j){
                x[j]=p[i][x[j]];
            }
        }
        k>>=1;
    }
    for(int i=1;i<=n;++i){
        cout<<a[x[i]]<<" ";
    }
    cout<<endl;
}

F.Rearrange Query \(\text{Diff }1540\)

给你两个序列 \(A,B\)

每次询问给定 \(l,r,L,R\),判断是否满足下列条件

\(A_{l},A_{l+1}\cdots A_{r}\) 可以通过重排与 \(B_{L},B_{L+1}\cdots B_{R}\) 匹配

\(N,Q\le 2\times 10^{5},\forall A_{i},B_{i}\in[1,N]\)

当 \(R-L\neq r-l\) 时显然不可能,考虑剩下的情况

设计一个哈希值,考虑每次直接 \(O(n)\) 判等. 那么我们这个哈希值显然是需要无序的,即只要是 \(A\) 的一个排列,其哈希值都与 \(A\) 相等. 可以想到两种哈希:和哈希与异或哈希.

显然异或哈希做不到给每个元素都赋不同的值,因为它总共就只有 \(64\) 位可用,因此考虑和哈希,即将序列元素中的值加起来之和用于判断.

显然,假如我们真的用原数列做哈希的话,出题人随便就给卡了,因为即便是和哈希,哈希冲突的概率还是太高. 因此考虑开一个 map,把数字离散化,均匀分布到 \([1,mod-1]\) 范围内. 可以证明这样做的冲突概率是 \(\frac{2^{64}}{n^2}\)

此外,直接处理前缀和即可 \(O(1)\) 回答询问

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p=141592653589;
int n,q;
int a[200001],b[200001],val[200001],sum1[200001],sum2[200001];
signed main(){	
	cin>>n>>q;
    ios::sync_with_stdio(false);
    srand(time(0));
    for(int i=1;i<=n;++i){
        val[i]=(rand()*1ll*rand())%(p-1)+1;
    }
    for(int i=1;i<=n;++i){
        cin>>a[i];
        a[i]=val[a[i]];
        sum1[i]=sum1[i-1]+a[i];
    }
    for(int i=1;i<=n;++i){
        cin>>b[i];
        b[i]=val[b[i]];
        sum2[i]=sum2[i-1]+b[i];
    }
    for(int i=1;i<=q;++i){
        int l,r,L,R;
        cin>>l>>r>>L>>R;
        if((sum1[r]-sum1[l-1])%p!=(sum2[R]-sum2[L-1])%p) cout<<"No\n";
        else cout<<"Yes\n";
    }
}

标签:Atcoder,le,数组,Beginner,200001,int,哈希,367,dis
From: https://www.cnblogs.com/HaneDaCafe/p/18365271

相关文章

  • Atcoder Beginner Contest 367 C-F
    AtcoderBeginnerContest367C-FC-EnumerateSequences题意按字典序升序输出所有满足下列条件的序列数量。长度为\(N\)。第\(i\)个元素介于\(1\)与\(R_i\)之间。所有元素之和是\(K\)的倍数。思路搜索即可。搜索时记录当前选了哪些数和元素之和,最后搜......
  • ABC 367 题解
    AtCoderBeginnerContest367题解:\(Problem\hspace{2mm}A-Shout\hspace{2mm}Everyday\)题目链接opinion:~~code:#include<bits/stdc++.h>#definelllonglong#definepiipair<int,int>usingnamespacestd;lla,b,c;intmain(){ i......
  • AtCoder Beginner Contest 367
    A-ShoutEveryday(abc367A)题目大意高桥从\(A\)睡到\(B\),如果在\(C\)时,他醒着,他则会对章鱼烧发癫,问他今天是否发癫。解题思路由于只有\(24\)小时,直接枚举\(A\toB\),看看是否遍历到\(C\)即可。神奇的代码#include<bits/stdc++.h>usingnamespacestd;usingLL=......
  • AtCoder Beginner Contest 367 补题记录(A~F)
    很Easy一场,共计用时\(34\)minAconstintN=1000100;signedmain(){strings;cin>>s;intcnt=0;intn=s.size();if(s[n-1]=='0'&&s[n-2]=='0'&&s[n-3]=='0'){s.pop_back();s.p......
  • AtCoder Beginner Contest 367
    喜欢我\(\log_210^{18}=18\)吗?A#include<bits/stdc++.h>#defineebemplace_back#defineepemplaceusingnamespacestd;usingll=longlong;inta,b,c;intmain(){ cin.tie(0)->sync_with_stdio(0); cin>>a>>b>>......
  • 题解:AtCoder Beginner Contest 367
    总体情况A题意在AtCoder王国,居民们每天都要在\(A\)点大声喊出他们对章鱼烧的热爱。住在AtCoder王国的高桥每天\(B\)点睡觉,\(C\)点起床(\(24\)小时钟)。他醒着的时候可以喊出对章鱼烧的爱,但睡着的时候却不能。判断他是否每天都能喊出对章鱼烧的爱。这里,一天有\(24......
  • AtCoder Beginner Contest 361
    ABC361A-Insert题目传送门代码(签到题)#include<iostream>#include<string>#include<algorithm>usingnamespacestd;intn,m,x,a[111];intmain(){ cin>>n>>m>>x; for(inti=1;i<=n;++i)cin>>a[i]; for(inti=1;i&l......
  • AtCoder Beginner Contest 045
    A-Trapezoids#include<bits/stdc++.h>usingnamespacestd;usingi64=longlong;intmain(){ ios::sync_with_stdio(false),cin.tie(nullptr); inta,b,h; cin>>a>>b>>h; cout<<(a+b)*h/2; return0;}B-......
  • [AtCoder] E - Putting Candies
    ProblemLinkIfwepickA[i]the2ndtime,itmeanswehaveacycle.Proof:1sttimewepickA[i],thesumbeforeaddingA[i]isx;2ndtimewepickA[i],thesumbeforeaddingA[i]isy; Forthistohappenx%N==y%Nmusthold.Otherwisewewouldno......
  • 题解:AT_abc365_d [ABC365D] AtCoder Janken 3
    D-AtCoderJanken3题解题意:高桥和青木要玩石头剪刀布,给你一个长度为\(n\)的字符串\(s\),\(s\)表示青木在第\(i\)局游戏中的动作(R表示石头,P表示布,S表示剪刀。)。高桥不可以在任何一局中输给青木(即:高桥和青木只可以平局或高桥赢青木),且高桥第\(i\)局出的和第\(i-1\)局......