Educational Codeforces Round 81 (Rated for Div. 2)
A. Display The Number (CF 1295 A)
题目大意
给\(n\)个火柴棒,问摆出的最大数字是多少?摆出每个数字所需要的火柴棒参照红绿灯。
解题思路
贪心即可。不断放\(1\),最后如果剩下一个火柴棒则最高位变成\(7\)。
神奇的代码
#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) {
int kase; read(kase);
for (int i = 1; i <= kase; i++) {
int n;
read(n);
int a[100500]={0};
int cnt=-1;
while(n>=2){
a[++cnt]=1;
n-=2;
}
if (n==1) a[cnt]=7;
for(int i=cnt;i>=0;--i) putchar(a[i]+'0');
puts("");
}
return 0;
}
B. Infinite Prefixes (CF 1295 B)
题目大意
给定一个\(01\)串\(s\),串\(t\)由串\(s\)不断重复得到,是无限长的。定义函数\(f(x)=cnt_0(x)-cnt_1(x)\),其中\(cnt_0(x)\)表示串\(t\)第一位到第\(x\)位\(0\)的个数,\(cnt_1\)同理。给定一个数\(x\),问有多少个\(i\)使得\(f(i)=x\),无限则输出\(-1\)
解题思路
由于\(s\)串是\(t\)串不断重复得到,\(x=f(j)=k*f(n)+f(i)\),其中\(k=\lfloor \frac{j}{n} \rfloor ,i=j\%n\),%是求余,且\(f(0)=0\),所以\(k=\dfrac{x-f(i)}{f(n)}\),对于每一个\(i \in [1,n]\),我们只要判断是否\((x-f(i))*f(n)>0\)且\((x-f(i))\%f(n)==0\)即可。当\(f(n)\)为\(0\)且\(f(1)\)到\(f(n)\)中存在等于\(x\)的则答案无限。注意如果\(x=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);
}
int main(void) {
int kase; read(kase);
for (int i = 1; i <= kase; i++) {
int n;
LL x;
read(n);
read(x);
char s[n]={0};
LL sum[n]={0};
scanf("%s",s);
sum[0]=(s[0]=='0')?1:-1;
bool qwq=sum[0]==x?true:false;
for(int i=1;i<n;++i) {
sum[i]=sum[i-1]+((s[i]=='0')?1:-1);
if (sum[i]==x) qwq=true;
}
if (sum[n-1]==0&&qwq) {puts("-1"); continue;}
else if (sum[n-1]==0&&!qwq) {puts("0"); continue;}
if (x*sum[n-1]<0&&!qwq) {puts("0"); continue;}
LL ans=0;
for(int i=0;i<n;++i){
if ((x-sum[i])*sum[n-1]<0) continue;
if ((x-sum[i])%sum[n-1]==0) ans++;
}
if (x==0) ++ans;
write(ans,'\n');
}
return 0;
}
C. Obtain The String (CF 1295 C)
题目大意
给定两个串\(s,t\),构造一个串\(z\)等于\(t\),每次取\(s\)的子串加到\(z\)串的最后,问最少取多少次\(s\)的子串。子串是指去掉\(s\)串任意个字母,在不改变剩下字母的相对位置得到的串。无法构造输出\(-1\)
解题思路
那\(set\)储存\(s\)串中每个字母的位置,记录当前处理的串\(s\)的位置,模拟就好了。当\(t\)串有\(s\)串中不存在的字母则不可构造。
神奇的代码
#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;
char s[N],t[N];
int main(void) {
int kase; read(kase);
for (int i = 1; i <= kase; i++) {
set<int> pos[26];
scanf("%s%s",s,t);
int ls=strlen(s);
bool sign[27]={0};
for(int i=0;i<ls;++i) pos[s[i]-'a'].insert(i),sign[s[i]-'a']=1;
int ans=1,cur=-1;
int lt=strlen(t);
bool qwq=true;
for(int i=0;i<lt;++i){
int x=t[i]-'a';
if (sign[x]==false) {qwq=false; break;}
auto a=pos[x].upper_bound(cur);
if (a==pos[x].end()){
ans++;
cur=(*pos[x].begin());
}else cur=*a;
}
if (qwq) write(ans,'\n');
else puts("-1");
}
return 0;
}
D. Same GCDs (CF 1295 D)
题目大意
给定\(a,m\),求\(\sum\limits_{x=0}^{m-1}[gcd(a,m)=gcd(a+x,m)]\)。
解题思路
设\(gcd(a,m)=n\),则\(a=k_1\times n,m=k_2\times n\),若\(gcd(a,m)=gcd(a+x,m)\),则\(a+x=k_1\times n+x=k_3\times n\),且\(gcd(k_3,k_2)=1\),即\(k_3\)与\(k_2\)互质,其中\(k_1 \leq k_3 \leq \lfloor \dfrac{a+m-1}{x} \rfloor\)。那问题就转化成给定一个数\(m\),求区间\([a,b]\)有多少个数与\(m\)互质,容斥即可。
当然\(\sum\limits_{i=1}^{a}[gcd(i,m)=1]=\sum\limits_{d|m}\mu(d)\times \frac{a}{d}\)莫比乌斯或许也可以
神奇的代码
#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);
}
vector<LL> prim;
LL a,m;
void divide(LL x){
prim.clear();
int qwq=sqrt(x);
for(int i=2;i<=qwq;++i)
if (x%i==0){
prim.push_back(i);
while(x%i==0) x/=i;
}
if (x>1) prim.push_back(x);
}
LL solve(LL x){
vector<LL> que;
que.push_back(-1);
for(size_t i=0;i<prim.size();++i){
int k=que.size();
for(int j=0;j<k;++j)
que.push_back(prim[i]*que[j]*(-1));
}
LL ans=0;
for(size_t i=1;i<que.size();++i)
ans+=x/que[i];
return x-ans;
}
int main(void) {
int kase; read(kase);
for (int i = 1; i <= kase; i++) {
read(a);
read(m);
LL x=__gcd(a,m);
LL u=(a+m-1)/x;
a/=x;
m/=x;
divide(m);
LL ans=solve(u)-solve(a-1);
write(ans,'\n');
}
return 0;
}
~~讲个笑话我内存0 B~~
E. Permutation Separation (CF 1295 E)
题目大意
给定一个排列\(p\),和一个数组\(a\)表示这个排列的每个位置的能量值,要求从中间某个位置把排列分成左右两段,然后把左边的一些数移动到右边,右边的一些数移动到左边,使得左边的所有值小于右边的所有值。某个值移动的代价为该位置的能量值。求满足条件所需要的最小能量值。注意,如果有一边没有数,我们也认为这满足了上述的条件(前提假则整个命题为真嘛)。
解题思路
我们首先发现,如果左边一段的数的个数是确定的,假设是\(k\),则最终左边的数一定是\(1\)$k$,右边的数一定是$k+1$\(n\),那么我们先枚举左边一段的数的个数\(k\),然后再枚举分割点\(i\)(表示第\(i\)个数的右边分割),再计算需要的能量值\(ans=\sum\limits_{j\leq i且q_j>k}a_j+\sum\limits_{j>i且q_j\leq k}a_j\),时间复杂度\(O(n^3)\)。
但我们注意到分割点移动的时候,只有分割点右边一个数对答案的贡献改变,于是可以\(O(1)\)更新答案,时间复杂度\(O(n^2)\)。
仔细分析可以发现,当\(k\)增加时,只有一个数,即\(k+1\)的归宿才从右边移动到左边,其他数都不变,那我们考虑这个数的归宿改变对答案的影响,可以发现,对于分割点在它左边的答案要增加它的能量值\(a_{k+1}\),以让它从右边移动到左边,而分割点在它右边的答案要减去它的能量值\(a_{k+1}\),以消除原来让它从左边移动到右边的所需要的能量。区间修改,用线段树维护答案即可。时间复杂度\(O(n\log_{2}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;
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=2e5+8;
struct Segment_Tree{
#define lson root<<1
#define rson root<<1|1
LL mark[4*N],minn[4*N];
void build(int root,int l,int r,LL *sum){
if (l==r){
minn[root]=sum[l];
mark[root]=0;
return;
}
int mid=(l+r)>>1;
build(lson,l,mid,sum);
build(rson,mid+1,r,sum);
minn[root]=min(minn[lson],minn[rson]);
mark[root]=0;
}
void pushdown(int root){
mark[lson]+=mark[root];
mark[rson]+=mark[root];
minn[lson]+=mark[root];
minn[rson]+=mark[root];
mark[root]=0;
}
void updata(int root,int l,int r,int ll,int rr,LL x){
if (ll>rr) return;
if (ll<=l&&r<=rr){
minn[root]+=x;
mark[root]+=x;
return;
}
pushdown(root);
int mid=(l+r)>>1;
if (ll<=mid) updata(lson,l,mid,ll,rr,x);
if (rr>mid) updata(rson,mid+1,r,ll,rr,x);
minn[root]=min(minn[lson],minn[rson]);
}
LL getans(int root,int l,int r,int ll,int rr){
if (ll<=l&&r<=rr) return minn[root];
pushdown(root);
int mid=(l+r)>>1;
if (rr<mid) return getans(lson,l,mid,ll,rr);
else if (ll>=mid) return getans(rson,mid+1,r,ll,rr);
else return min(getans(lson,l,mid,ll,rr),getans(rson,mid+1,r,ll,rr));
}
}Segment;
int n;
int pos[N];
LL sum[N],a[N];
int main(void) {
read(n);
for(int u,i=1;i<=n;++i){
read(u);
pos[u]=i;
}
for(int i=1;i<=n;++i){
read(a[i]);
sum[i]=a[i]+sum[i-1];
}
Segment.build(1,1,n-1,sum);
LL ans=Segment.getans(1,1,n-1,1,n-1);
for(int i=1;i<=n;++i){
Segment.updata(1,1,n-1,1,pos[i]-1,a[pos[i]]);
Segment.updata(1,1,n-1,pos[i],n-1,-a[pos[i]]);
ans=min(ans,Segment.getans(1,1,n-1,1,n-1));
}
write(ans,'\n');
return 0;
}