2019-12-20 20:49阅读: 490评论: 4推荐: 1

Educational Codeforces Round 78 (Rated for Div. 2)

A. Shuffle Hashing (CF 1278 A)

题目大意

给定两个字符串a,b,现改变a串中的字母顺序,问是否存在某种顺序在b串中出现。

解题思路

先对a进行排序,由于字符串长度最多只有100,我们就枚举b串中的起始位置,然后把长度为a.size()的子串截取下来排序看看是否和a相等即可。复杂度O(n2logn)
(CF上的dalao的思维好快qwq)

神奇的代码
#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)

题目大意

给定两个数a,b,第一次选ab1,第二次选ab2,第三次选ab3,依次类推,问最少多少次操作使得a,b相等。

解题思路

dis=abs(ab),我们进行了n次操作,这n次操作中有若干次是使dis变大,有若干次是使dis变小,最终应有dis+xy=0,其中x+y=n(n+1)2,联立这两个式子消去ydis+2x=n(n+1)2
这是个关于n的二次函数,其中n是单调递增的,那么我们需要找到最小的非负整数x,使得n是非负整数即可。
解这个一元二次方程即可得到

n=1+1+16x+8dis2(0)

我们对x0开始枚举找到第一个1+16x+8dis是某奇数的平方即可(代码被注释的那段)
然后我看了下CF上的前排dalao们发现他们写的思路清奇且一模一样0.0
由于x是非负数所以有n(n+1)2dis,然后我们对等式dis+2x=n(n+1)2两边对2取模,即可得到dis,所以我们对n1开始,找到第一个n使得n(n+1)2disdisn(n+1)2的奇偶性相同,那么就一定存在一个非负整数x使得dis+2x=n(n+1)2
(这就是大佬qwq)

神奇的代码
#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)

题目大意

2n个数字,每个数字是1或者2,现在从中间开始,每次往左或往右删去最近的一个数,现需要使12的个数相等,求最小删除数的个数。

解题思路

我们枚举往左删除的端点,看看右端点最短要延伸到哪里。假设左端点位置为i,除去[i,n]12的个数后,12的差值为dis,我们需要知道最小的右端点r,使得区间[n+1,r]12的差值也为dis,这样我们除去[n+1,r]中的12后,剩余的12的个数就相等了。而最小的r我们可以用unordered_map来维护,记录右半部分12的差值为d的最小位置为map[d],那此时删除的个数就是(ni+1)+map[d]n。这样复杂度就是O(n)了。
其实由于n最多1e5,我们开个4e5的数组来记录位置也是可以的。

神奇的代码
#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条线段,端点值互不相同,如果两条线段有交叉部分(包含不算),则这两条线段之间连一条边。问最终这些线段形成的图是不是棵树。

解题思路

注意到线段右端点1l<r2n,这就意味着所有线段的两个端点的取值都在[1,2n]中。
我们先对线段的左端点进行排序,依次考虑每个线段的与哪些线段有连边。
当前考虑的是第i条线段,由于连边条件具有对称性,我们可以只考虑第1条到第i1条线段与第i条线段。
由于前面的线段的左端点l均小于li,那么我们只要找到其右端点大于li且小于ri的线段,它们会有连边。而我们要找的右端点是个连续的范围,那么我们可以把1i1的线段的右端点丢到set里面,这样我们就可以在O(logn)的时间内找到对应的线段。
而当连的边数大于等于n时,此时不可能是棵树,直接break了。最后再判断边数是否是n1并再DFS遍历一遍看看是不是一个连通块即可。复杂度O(nlogn)

神奇的代码
#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)

题目大意

上一题的逆向,给定一棵带标号的树,要求构造一组线段使之按照上题连线的规则,形成给定的树。输出每个标号对应的线段的左右端点。端点唯一且小于等于2n.

解题思路

我们考虑第一个线段,l1=1,而因为第一个线段与k1个线段有连线,故r1l11=k1,即第一个线段之间的点用来存放与第一条线段相连的左端点。
然后我们考虑与第一条线段相连的线段,稍加分析可以知道从第一条线段右端点往左开始考虑会比较方便。
则对于第二条线段,它的l2=r11,除去第一条线段,第二条线段与k2条线段相连,那么r2l11=k2。其他的依次类推,最后我们可以形成一张

看似比较复杂的构造方法,其实用DFS即可很容易的实现。
我们依次为与一个线段相连的线段的左端点安排位置,安排完后下一个位置就是该线段右端点的位置,然后倒序遍历与这个线段相连的线段,重复同样操作即可。

神奇的代码
#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)

题目大意

给定n,m,k,有m张牌,其中一张为鬼牌。现进行n次操作,每次操作,对m张拍进行洗牌,从上到下,拿第一张牌,看牌,放牌。记拿的第一张牌是鬼牌的次数为x,问xk的期望值。

解题思路

根据期望的定义我们可以很容易知道答案就是:

i=0nCni×(1m)i×(11m)ni×ik

但直接算会超时,我们得另想办法。

经过观察我们发现这个式子和二项式展开式(xm+11m)n=i=0nCni×(1m)i×(11m)ni×xi十分相像,差别就是ik

我们可以先对这个式子两边求一阶导,这样右边就变成了i=0nCni×(1m)i×(11m)ni×i×xi1,我们对等式两边再乘以x,再求一次导,重复k次,我们右边就有ik,此时取x=1,右边就是我们想要的式子了,现在考虑左边该如何计算。

由于m是常数,我们把m扔到一边,考虑(x+m1)n在求导以及乘以x下的变化。

我们可以发现得到的因子都是形如xi×(x+m1)ni,那么我们设Ai=xi×(x+m1)ni,它的系数为Bijj表示求了j次导。那么BijAi求导再乘以x后产生了两项,一项是i×Bij×Ai贡献给了Bi(j+1)×Ai,另一项(ni)×Bij×Ai+1贡献给了B(i+1)(j+1)×Ai+1

因此我们可以O(k2)计算出求导了k次后,各个Ai的系数,代入x=1,计算结果,最后再除以mn即可得到最终答案。

注意当n<k的时候,An求导后的贡献没有An+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);
}
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 中国大陆许可协议进行许可。

posted @   ~Lanly~  阅读(490)  评论(4编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.