Educational Codeforces Round 78 (Rated for Div. 2)
A. Shuffle Hashing (CF 1278 A)
题目大意
给定两个字符串,现改变串中的字母顺序,问是否存在某种顺序在串中出现。
解题思路
先对进行排序,由于字符串长度最多只有,我们就枚举串中的起始位置,然后把长度为的子串截取下来排序看看是否和相等即可。复杂度
上的的思维好快。
神奇的代码
#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; bool check(void) { string a,b; cin>>a>>b; sort(a.begin(),a.end()); int la=a.size(); int lb=b.size(); for(int i=0;i<lb;++i){ if (i+la>lb) break; string c(b,i,la); sort(c.begin(),c.end()); if (a==c) return true; } return false; } 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; cin>>kase; while(kase--) if (check()) printf("YES\n"); else printf("NO\n"); return 0; }
B. A and B (CF 1278 B)
题目大意
给定两个数,第一次选或加,第二次选或加,第三次选或加,依次类推,问最少多少次操作使得相等。
解题思路
令,我们进行了次操作,这次操作中有若干次是使变大,有若干次是使变小,最终应有,其中,联立这两个式子消去得
这是个关于的二次函数,其中是单调递增的,那么我们需要找到最小的非负整数,使得是非负整数即可。
解这个一元二次方程即可得到
我们对从开始枚举找到第一个是某奇数的平方即可(代码被注释的那段)
然后我看了下上的前排们发现他们写的思路清奇且一模一样
由于是非负数所以有,然后我们对等式两边对取模,即可得到,所以我们对从开始,找到第一个使得且和的奇偶性相同,那么就一定存在一个非负整数使得
这就是大佬。
神奇的代码
#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); } /* long long a,b,dis,qwq; bool check(long long x){ long long qaq=sqrt(x); if (qaq*qaq==x) { if (qaq&1) return true; } else return false; } void Input(void) { read(a); read(b); //if (dis==0) {printf("0\n"); return;} dis=8ll*ABS(a-b)+1ll; for(int i=0;1;++i){ if (check(dis+16*i)) {qwq=sqrt(dis+16*i); break;} } printf("%lld\n",((qwq-1ll)/2ll)); } */ void Input(void){ long long a,b; read(a); read(b); long long dis=ABS(a-b); long long sum=0,cnt=0; while(sum<dis||((sum&1)!=(dis&1))) sum+=++cnt; printf("%lld\n",cnt); } void Solve(void) {} void Output(void) {} int main(void) { //ios::sync_with_stdio(false); 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); Input(); Solve(); Output(); } return 0; }
C. Berry Jam (CF 1278 C)
题目大意
个数字,每个数字是或者,现在从中间开始,每次往左或往右删去最近的一个数,现需要使和的个数相等,求最小删除数的个数。
解题思路
我们枚举往左删除的端点,看看右端点最短要延伸到哪里。假设左端点位置为,除去中和的个数后,和的差值为,我们需要知道最小的右端点,使得区间中和的差值也为,这样我们除去中的和后,剩余的和的个数就相等了。而最小的我们可以用来维护,记录右半部分和的差值为的最小位置为,那此时删除的个数就是。这样复杂度就是了。
其实由于最多,我们开个的数组来记录位置也是可以的。
神奇的代码
#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); } unordered_map<int,int> qwq; int n,dis,ans; const int N=1e5+8; int a[N],sum[3]; void Input(void) { qwq.clear(); read(n); sum[1]=sum[2]=0; for(int i=1;i<=n;++i) { read(a[i]); ++sum[a[i]]; } int s[3]; s[1]=s[2]=0; for(int u,i=1;i<=n;++i){ read(u); ++s[u]; if (qwq[s[1]-s[2]]==0) qwq[s[1]-s[2]]=i; } sum[1]+=s[1]; sum[2]+=s[2]; } void Solve(void) { dis=sum[1]-sum[2]; if (dis==0) ans=0; else{ if (qwq[dis]) ans=qwq[dis]; else ans=2147483647; for(int i=n;i>=1;--i){ if (a[i]==1) --dis; else ++dis; if (dis==0) ans=MIN(ans,n-i+1); else if (qwq[dis]) ans=MIN(ans,n-i+1+qwq[dis]); } } printf("%d\n",ans); } void Output(void) {} int main(void) { //ios::sync_with_stdio(false); 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); Input(); Solve(); Output(); } return 0; }
D. Segment Tree (CF 1278 D)
题目大意
真·线段树
给定n条线段,端点值互不相同,如果两条线段有交叉部分(包含不算),则这两条线段之间连一条边。问最终这些线段形成的图是不是棵树。
解题思路
注意到线段右端点,这就意味着所有线段的两个端点的取值都在中。
我们先对线段的左端点进行排序,依次考虑每个线段的与哪些线段有连边。
当前考虑的是第条线段,由于连边条件具有对称性,我们可以只考虑第条到第条线段与第条线段。
由于前面的线段的左端点均小于,那么我们只要找到其右端点大于且小于的线段,它们会有连边。而我们要找的右端点是个连续的范围,那么我们可以把到的线段的右端点丢到里面,这样我们就可以在的时间内找到对应的线段。
而当连的边数大于等于时,此时不可能是棵树,直接了。最后再判断边数是否是并再遍历一遍看看是不是一个连通块即可。复杂度
神奇的代码
#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=5e5+8; set<pair<int,int>> qwq; vector<pair<int,int>> line; vector<int> edge[N]; vector<bool> sign; int n; void DFS(int x){ sign[x]=true; for(auto i:edge[x]) if (!sign[i]) DFS(i); } bool Solve(void) { read(n); for(int u,v,i=1;i<=n;++i){ read(u); read(v); line.push_back(make_pair(u,v)); } sort(line.begin(),line.end()); int cnt=0; for(int i=0;i<n;++i){ auto it=qwq.lower_bound(make_pair(line[i].first,0)); while(it!=qwq.end()&&(*it).first<=line[i].second){ ++cnt; if (cnt>=n) return false; edge[i].push_back((*it).second); edge[(*it).second].push_back(i); ++it; } qwq.insert(make_pair(line[i].second,i)); } if (cnt<n-1) return false; sign.resize(n); DFS(0); for(auto i:sign) if (i==false) return false; return true; } int main(void) { freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); if (Solve()) printf("YES\n"); else printf("NO\n"); return 0; }
E. Tests for problem D (CF 1278 E)
题目大意
上一题的逆向,给定一棵带标号的树,要求构造一组线段使之按照上题连线的规则,形成给定的树。输出每个标号对应的线段的左右端点。端点唯一且小于等于.
解题思路
我们考虑第一个线段,,而因为第一个线段与个线段有连线,故,即第一个线段之间的点用来存放与第一条线段相连的左端点。
然后我们考虑与第一条线段相连的线段,稍加分析可以知道从第一条线段右端点往左开始考虑会比较方便。
则对于第二条线段,它的,除去第一条线段,第二条线段与条线段相连,那么。其他的依次类推,最后我们可以形成一张
看似比较复杂的构造方法,其实用即可很容易的实现。
我们依次为与一个线段相连的线段的左端点安排位置,安排完后下一个位置就是该线段右端点的位置,然后倒序遍历与这个线段相连的线段,重复同样操作即可。
神奇的代码
#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=5e5+8; vector<int> edge[N]; int n,k; int l[N],r[N]; void DFS(int u,int fa){ for(auto v:edge[u]) if (v!=fa) l[v]=++k; r[u]=++k; reverse(edge[u].begin(),edge[u].end()); for(auto v:edge[u]) if (v!=fa) DFS(v,u); } int main(void) { freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); read(n); for(int u,v,i=1;i<=n;++i){ read(u); read(v); edge[u].push_back(v); edge[v].push_back(u); } k=0; l[1]=++k; DFS(1,1); for(int i=1;i<=n;++i) printf("%d %d\n",l[i],r[i]); return 0; }
F. Cards (CF 1278 F)
题目大意
给定,有张牌,其中一张为鬼牌。现进行次操作,每次操作,对张拍进行洗牌,从上到下,拿第一张牌,看牌,放牌。记拿的第一张牌是鬼牌的次数为,问的期望值。
解题思路
根据期望的定义我们可以很容易知道答案就是:
但直接算会超时,我们得另想办法。
经过观察我们发现这个式子和二项式展开式十分相像,差别就是。
我们可以先对这个式子两边求一阶导,这样右边就变成了,我们对等式两边再乘以,再求一次导,重复次,我们右边就有,此时取,右边就是我们想要的式子了,现在考虑左边该如何计算。
由于是常数,我们把扔到一边,考虑在求导以及乘以下的变化。
我们可以发现得到的因子都是形如,那么我们设,它的系数为,表示求了次导。那么求导再乘以后产生了两项,一项是贡献给了,另一项贡献给了。
因此我们可以计算出求导了次后,各个的系数,代入,计算结果,最后再除以即可得到最终答案。
注意当的时候,求导后的贡献没有的项。
神奇的代码
#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 LL mo=998244353; LL qpower(LL a,LL b){ LL qwq=1; while(b){ if (b&1) qwq=qwq*a%mo; a=a*a%mo; b>>=1; } return qwq; } LL inv(LL x){ return qpower(x,mo-2); } int main(void) { LL n,m,k; read(n); read(m); read(k); LL a[k+8]={0}; a[0]=1; for(int i=1;i<=k;++i){ for(int j=i-1;j>=0;--j){ if (j<n) a[j+1]=(a[j+1]+a[j]*(LL)(n-j)%mo)%mo; a[j]=a[j]*(LL)j%mo; } } LL qwq=qpower(m,max(n-k,0ll)); LL ans=0; for(int i=min(n,k);i>=0;--i){ ans=(ans+qwq*a[i])%mo; qwq=qwq*m%mo; } ans=ans*inv(qpower(m,n))%mo; write(ans,'\n'); return 0; }
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/12074845.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步