0%

6月19日 Codeforces Round #489 题目链接:http://codeforces.com/contest/992

A - Nastya and an Array

过水,不多说。

B - Nastya Studies Informatics

思路: 思路比较暴力,一开始以为不是正解,复杂度似乎是$O(\sqrt{n}log(n))$。

首先想到的便是暴力枚举,枚举a,b则可以通过计算得到($b=xy/a$),然后判断$a,b$是否再范围内并且$gcd(a,b)$是否等于x。

然后便发现可以优化,可以将l,r,y同除x(注意取整)。如果x,y互素,则一定为0。然后就可以从1枚举到$\sqrt{y/x}$,判断a,b是否互素。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <bits/stdc++.h>
#define LL long long
using namespace std;

LL l, r, x, y, s, ans;

bool check(LL a, LL b)
{
if(a>=l && a<=r && b>=l && b<=r) return true;
else return false;
}

int main()
{
cin>>l>>r>>x>>y;
if(y%x!=0)
{
printf("0\n");
return 0;
}
l=(l+x-1)/x; r=r/x; y/=x;
for(int i=1; i<=sqrt(y); i++)
{
if(y%i!=0) continue;
int j=y/i;
if(check(i, j) && __gcd(i, j)==1)
{
if(i*i==y) ans++;
else ans+=2;
}
}
cout<<ans<<endl;
}

C - Nastya and a Wardrobe

思路: 结果还是数学题,我们可以考虑所有情况,k个月时可能的衣服数量是从$n*2^k$到$(n-1)*2^k+1$。

因为它们是等概率分布。所以答案是$n*2^k+(n-1)*2^k+1$。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <bits/stdc++.h>
#define LL long long
#define p 1000000007
using namespace std;

LL n, k;

LL ksm(LL x, LL y)
{
LL sum=1;
while(y)
{
if(y&1) sum=(sum*x)%p;
x=(x*x)%p; y>>=1;
}
return sum%p;
}

int main()
{
cin>>n>>k;
if(n==0) cout<<0;
else cout<<((n%p*ksm(2, k))%p+((n-1)%p*ksm(2, k))%p+1)%p;
}

D - Nastya and a Game

思路: 就是暴力啊。。比赛时想到了$O(n*n)$的暴力,其实稍微改一下就能A了。$O(n*n)$的暴力是这样的:先枚举L,记录下从L开始的乘积以及总和,只要符合要求就更新答案。

但是这样就会出现一些问题,首先是乘积会炸longlong,但是由于数据是构造好了的,只要乘积大于了10^18是一定不符合要求的,这时候就可以break了。、

这样的复杂度是不符合要求的,如果序列全是1,复杂度还是$O(n*n)$的。但是我们考虑因为1是不会影响乘积的,如果我们把序列中的1去掉,同样的暴力方法,最多枚举64个数字(64个数字全是2)就一定会break。这样的话,最坏复杂度就是$O(64*n)$。

最后,判断符合要求时要考虑序列中删去的1,写个check()函数就好了。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <bits/stdc++.h>
#define LL long long
#define INF 1e18
using namespace std;

LL n, k, a[200005], nxt[200005], ans;

bool check(LL sum, LL pro, LL len)
{
LL temp=pro-sum*k;
if(temp%k) return false;
if(temp/k>=0 && temp/k<=len) return true;
else return false;
}

int main()
{
cin>>n>>k;
for(int i=1; i<=n; i++) cin>>a[i];
for(int i=n, pos=n+1; i>=1; i--)
{
nxt[i]=pos;
if(a[i]>1) pos=i;
}
for(int i=1; i<=n; i++)
{
LL pro=1, sum=0;
for(int j=i; j<=n; j=nxt[j])
{
if(pro>=INF/a[j]) break;
pro*=a[j]; sum+=a[j];
if(check(sum, pro, nxt[j]-j-1)) ans++;
sum+=nxt[j]-j-1;
}
}
cout<<ans<<endl;
}

E - Nastya and King-Shamans

思路: 超级神奇的数据结构题,在一个半月后终于A了。。前缀和处理应该不难想到,由于涉及修改操作,所以我们用树状数组维护前缀和。所以我们对于每一个询问要找到一个位置$x$,使得$sum[x-1]*2=sum[x]$($sum$为前缀和)。

我们查找位置$x$可以利用分治的思想。对于任意一个区间$[l,r]$($lsum[r]$,不难发现这个区间内一定不会存在$x$。于是我们可以将整个区间$[0,n]$不断分治,直到找到位置$x$。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define LL long long
#define INF 1e18
#define MAXN 200005
using namespace std;

int n, q;
LL t[MAXN], a[MAXN];

void add(int x, LL y) {for(; x<=n; x+=(x&-x)) t[x]+=y;}

LL query(int x) {LL y=0; for(; x; x-=(x&-x)) y+=t[x]; return y;}

int find(int l, int r)
{
int pos=-1, mid=(l+r)>>1;
if(l+1==r) return query(l)*2==query(r)? r:-1;
if(query(l)*2<=query(mid)) pos=max(pos, find(l, mid));
if(pos!=-1) return pos;
if(query(mid)*2<=query(r)) pos=max(pos, find(mid, r));
return pos;
}

int main()
{
scanf("%d%d", &n, &q);
for(int i=1; i<=n; i++)
{
scanf("%lld", &a[i]);
add(i, a[i]);
}
for(int i=1; i<=q; i++)
{
int p;LL x;
scanf("%d %lld", &p, &x);
add(p, x-a[p]); a[p]=x;
printf("%d\n", find(0, n));
}
}

6月16日 AtCoder Beginner Contest 题目链接:https://abc100.contest.atcoder.jp/

A - Happy Birthday! & B - Ringo’s Favorite Numbers

比较水。。。 B题一定要弄清楚题意!!!注意特判!!!

C - *3 or /2

思路: 因为题目希望操作次数尽量多,所以我们可以贪心,每次只让一个数除2,其余乘3。那么这样,总操作次数便是每个数能够除以2的次数之和。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <cstdio>
#include <algorithm>
#include <cmath>
#define LL long long
using namespace std;

LL n, ans, a[10005];

int main()
{
scanf("%lld", &n);
for(int i=1; i<=n; i++)
{
scanf("%d", &a[i]);
while(a[i]%2==0)
{
a[i]/=2;
ans++;
}
}
printf("%d\n", ans);
}

D - Patisserie ABC

思路: 这道题的做法稍稍有一点暴力。首先可以枚举三个值的正负情况,然后就可以计算每一个蛋糕对于三个值的贡献之和。排一个序,取前m个蛋糕的贡献之和用来更新答案。

代码;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define LL long long
using namespace std;

int n, m, act[5], q[1005];
LL ans, b[1005], a[1005][5];

bool CMP(LL x, LL y) {return x>y;}

void dfs(int x)
{
if(x==3)
{
LL temp=0; memset(b, 0, sizeof(b));
for(int i=0; i<3; i++)
{
if(act[i]==1) for(int j=1; j<=n; j++) b[j]+=a[j][i];
else for(int j=1; j<=n; j++) b[j]-=a[j][i];
}
sort(b+1, b+n+1, CMP);
for(int i=1; i<=m; i++) temp+=b[i];
if(temp>ans) ans=temp;
return;
}
for(int i=1; i<=2; i++)
{
act[x]=i;
dfs(x+1);
}
}

int main()
{
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++) scanf("%lld%lld%lld", &a[i][0], &a[i][1], &a[i][2]);
dfs(0);
printf("%lld\n", ans);
}

问题描述:

小Z把这N只袜子从1到N编号,然后从编号L到R中抽两只袜子。你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。他可能会询问多个(L,R)以方便自己选择。

输入:

第一行包含两个正整数N和M。N为袜子的数量,M为小Z所提的询问的数量。 接下来一行包含N个正整数Ci,其中Ci表示第i只袜子的颜色,相同的颜色用相同的数字表示。 再接下来M行,每行两个正整数L,R表示一个询问。

输出:

包含M行,对于每个询问在一行中输出分数A/B表示从该询问的区间[L,R]中随机抽出两只袜子颜色相同的概率。若该概率为0则输出0/1,否则输出的A/B必须为最简分数。

思路:

这道题可以说是莫队的模板了。首先考虑如何计算概率,在给定的区间内抽到两只颜色相同的袜子的概率是$ans=(Σ(sum(color[i])^2)−(R−L+1))/((R−L+1)∗(R−L))$。

然后就使用莫队,先将问题离排序,离线操作,写一个update函数进行转移。详细内容可以看代码啦。

最后输出答案时应输出最简分数,记得要分子分母同除GCD。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <bits/stdc++.h>
#define MAXN 50005
#define LL long long
using namespace std;

int n, m, l=1, r, size, col[MAXN], belong[MAXN];
LL ans, sum[MAXN];

struct Q {int l, r, id; LL x, y;} q[MAXN];

bool CMP1(Q x, Q y)
{
if(belong[x.l]==belong[y.l]) return x.r<y.r;
else return x.l<y.l;
}

bool CMP2(Q x, Q y) {return x.id<y.id;}

void update(int pos, int opt)
{
ans-=sum[col[pos]]*sum[col[pos]];
sum[col[pos]]+=opt;
ans+=sum[col[pos]]*sum[col[pos]];
}

int main()
{
scanf("%d%d", &n, &m);
size=sqrt(n);
for(int i=1; i<=n; i++)
{
scanf("%d", &col[i]);
belong[i]=i/size+1;
}
for(int i=1; i<=m; i++)
{
scanf("%d%d", &q[i].l, &q[i].r);
q[i].id=i;
}
sort(q+1, q+m+1, CMP1);
for(int i=1; i<=m; i++)
{
while(l<q[i].l) update(l++, -1);
while(l>q[i].l) update(--l, 1);
while(r<q[i].r) update(++r, 1);
while(r>q[i].r) update(r--, -1);
if(q[i].l==q[i].r)
{
q[i].x=0; q[i].y=1;
continue;
}
q[i].x=ans-q[i].r+q[i].l-1;
q[i].y=(LL)(q[i].r-q[i].l+1)*(q[i].r-q[i].l);
LL t=__gcd(q[i].x, q[i].y);
q[i].x/=t; q[i].y/=t;
}
sort(q+1, q+m+1, CMP2);
for(int i=1; i<=m; i++) printf("%lld/%lld\n", q[i].x, q[i].y);
}

6月11日 AtCoder Beginner Contest 099 题目链接:https://abc099.contest.atcoder.jp/

A - ABD & B - Stone Monument

过水,不多说

C - Strange Bank

思路: 一开始分析了下复杂度,以为迭代加深DFS可做。结果大样例跑了一万年QWQ。。

后面就想到了背包,完全背包直接从前向后扫一遍,就OK了。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <bits/stdc++.h>
#define LL long long
#define MAXN 100005
#define INF 0x3f3f3f3f
using namespace std;

int n, ans, f[MAXN];
bool flag;
int n6[100], n9[100];


int main()
{
memset(f, 0x3f, sizeof(f));
scanf("%d", &n);
for(int i=0; i<=7; i++) n6[i]=n9[i]=1;
for(int i=0; i<=7; i++)
{
for(int j=1; j<=i; j++) n6[i]*=6;
for(int j=1; j<=i; j++) n9[i]*=9;
}
f[0]=0;
for(int i=0; i<=n; i++)
{
for(int j=0; j<=7; j++)
{
if(i+n6[j]<=n) f[i+n6[j]]=min(f[i+n6[j]], f[i]+1);
if(i+n9[j]<=n) f[i+n9[j]]=min(f[i+n9[j]], f[i]+1);
}
}
printf("%d\n", f[n]);
}

D - Good Grid

思路: 开始想到暴力$n^2*c^3$,后面发现可以通过预处理将复杂度降为$n^2*c+c^3$,然后就可以过了。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <bits/stdc++.h>
#define LL long long
#define MAXN 300005
#define INF 2147483647
using namespace std;

int n, k, ans=INF, num[35][5];
int mp[505][505], c[35][35];

int main()
{
scanf("%d%d", &n, &k);
for(int i=1; i<=k; i++) for(int j=1; j<=k; j++) scanf("%d", &c[i][j]);
for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) scanf("%d", &mp[i][j]);
for(int x=1; x<=k; x++)
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++) num[x][(i+j)%3]+=c[mp[i][j]][x];
}
}
for(int i=1; i<=k; i++)
{
for(int j=1; j<=k; j++)
{
for(int l=1; l<=k; l++)
{
if(i!=j && j!=l && l!=i) ans=min(ans, num[i][0]+num[j][1]+num[l][2]);
}
}
}
printf("%d\n", ans);
}

6月11日 Educational Codeforces Round 45 题目链接:http://codeforces.com/contest/990

A - Commentary Boxes & B - Micro-World

过水,不多说。

C - Bracket Sequences Concatenation Problem

思路: 类似于模拟吧,不过思路一定要清晰(我弄错了很多次QWQ)。

可以用一个map记录有多少序列有x个左(右)括号未匹配,然后答案就可以通过这个计算得到。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <bits/stdc++.h>
#define LL long long
#define MAXN 300005
#define INF 0x3f3f3f3f
using namespace std;

int n, l, r;
LL ans;
map<LL, LL> mp;
char s[MAXN];

int main()
{
//freopen("a.in", "r", stdin);
//freopen("a1.out", "w", stdout);
scanf("%d", &n);
for(int i=1; i<=n; i++)
{
scanf("%s", s);
int len=strlen(s), l=0, r=0;
for(int j=0; j<len; j++)
{
if(r==0 && s[j]==')') l--;
if(r!=0 && s[j]==')') r--;
if(l==0 && s[j]=='(') r++;
if(l!=0 && s[j]=='(') r++;
}
if(l==0 && r==0) mp[0]++;
else
{
if(l==0) mp[r]++;
if(r==0) mp[l]++;
}
}
for(int i=0; i<=MAXN; i++)
{
ans+=mp[i]*mp[-i];
}
cout<<ans;
}

D - Graph And Its Complement

思路: 是一道结论题吧。可以考虑当n>3时如果a,b均不等于1,则该情况不存在。当n=1或n=2并且a=b=1时同样无解。

其余情况都是有解的,然后就考虑输出方案。这时a或b有一个为1,如果b为1就直接构造,否则构造后输出它的反图。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include<bits/stdc++.h>
using namespace std;

int n, a, b, ans[1005][1005];
bool flag;

int main()
{
scanf("%d%d%d", &n, &a, &b);
if(a<b) swap(a, b), flag=1;
if(b!=1 || ((n==2 || n==3) && (a==1 && b==1))) {printf("NO\n"); return 0;}
printf("YES\n");
for(int i=1; i<=n-a; i++)
{
ans[i][i+1]=1;
ans[i+1][i]=1;
}
if(!flag)
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++) printf("%d", ans[i][j]);
printf("\n");
}
}
else
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
if(i==j) printf("0");
else printf("%d", 1^ans[i][j]);
}
printf("\n");
}
}

}

E - Post Lamps

思路: 也是一个比较简单的模拟+贪心。每一次放灯时尽量放右边,并且灯之间全要覆盖。要判一下能否符合要求,如果整个序列都符合要求,就更新一下答案。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <bits/stdc++.h>
#define INF 1e18
#define LL long long
#define lld I64d
#define MAXN 1000005
using namespace std;

LL n, m, k, temp, ans=INF, lef[MAXN], a[MAXN];
bool vis[MAXN];

LL solve(LL x)
{
LL r=0, cnt=0, pos=-1;
while(r<n)
{
if(pos>=lef[r]) return INF;
pos=lef[r];
r=pos+x;
cnt++;
}
return cnt;
}

int main()
{
scanf("%lld%lld%lld", &n, &m, &k);
for(int i=1; i<=m; i++)
{
scanf("%lld", &temp);
vis[temp]=true;
}
if(vis[0]) lef[0]=-1;
for(int i=1; i<n; i++)
{
if(vis[i]) lef[i]=lef[i-1];
else lef[i]=i;
}
for(int i=1; i<=k; i++) scanf("%lld", &a[i]);
for(int i=1; i<=k; i++)
{
temp=solve(i);
if(temp!=INF && ans>temp*a[i]) ans=temp*a[i];
}
if(ans==INF) printf("-1\n");
else printf("%lld\n", ans);
}