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} floor ,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,'
');
}
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,'
');
else puts("-1");
}
return 0;
}
D. Same GCDs (CF 1295 D)
题目大意
给定(a,m),求(sumlimits_{x=0}^{m-1}[gcd(a,m)=gcd(a+x,m)])。
解题思路
设(gcd(a,m)=n),则(a=k_1 imes n,m=k_2 imes n),若(gcd(a,m)=gcd(a+x,m)),则(a+x=k_1 imes n+x=k_3 imes n),且(gcd(k_3,k_2)=1),即(k_3)与(k_2)互质,其中(k_1 leq k_3 leq lfloor dfrac{a+m-1}{x}
floor)。那问题就转化成给定一个数(m),求区间([a,b])有多少个数与(m)互质,容斥即可。
当然(sumlimits_{i=1}^{a}[gcd(i,m)=1]=sumlimits_{d|m}mu(d) imes 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,'
');
}
return 0;
}
~~讲个笑话我内存0 B~~
E. Permutation Separation (CF 1295 E)
题目大意
给定一个排列(p),和一个数组(a)表示这个排列的每个位置的能量值,要求从中间某个位置把排列分成左右两段,然后把左边的一些数移动到右边,右边的一些数移动到左边,使得左边的所有值小于右边的所有值。某个值移动的代价为该位置的能量值。求满足条件所需要的最小能量值。注意,如果有一边没有数,我们也认为这满足了上述的条件(前提假则整个命题为真嘛)。
解题思路
我们首先发现,如果左边一段的数的个数是确定的,假设是(k),则最终左边的数一定是(1)~(k),右边的数一定是(k+1)~(n),那么我们先枚举左边一段的数的个数(k),然后再枚举分割点(i)(表示第(i)个数的右边分割),再计算需要的能量值(ans=sumlimits_{jleq i且q_j>k}a_j+sumlimits_{j>i且q_jleq k}a_j),时间复杂度(O(n^3))。
但我们注意到分割点移动的时候,只有分割点右边一个数对答案的贡献改变,于是可以(O(1))更新答案,时间复杂度(O(n^2))。
仔细分析可以发现,当(k)增加时,只有一个数,即(k+1)的归宿才从右边移动到左边,其他数都不变,那我们考虑这个数的归宿改变对答案的影响,可以发现,对于分割点在它左边的答案要增加它的能量值(a_{k+1}),以让它从右边移动到左边,而分割点在它右边的答案要减去它的能量值(a_{k+1}),以消除原来让它从左边移动到右边的所需要的能量。区间修改,用线段树维护答案即可。时间复杂度(O(nlog_{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,'
');
return 0;
}