比赛地址:Dashboard - Codeforces Round 955 (Div. 2, with prizes from NEAR!) - Codeforces

一个月没做了,期末周太忙了呜呜呜

D太遗憾了,结束7分钟后就出代码了,后来一测一发过,,前中段太唐了

A

题意:给出两个比分,一段时间后又给出一个比分,问这期间两队有没有可能出现平局的局面。

比较一下大小就行,水题。

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

int main()
{
int t;
cin>>t;
while(t--)
{
int a,b,c,d;
cin>>a>>b>>c>>d;
if(a<b)
{
if(c<d)cout<<"YES\n";
else cout<<"NO\n";
}
else
{
if(c>d)cout<<"YES\n";
else cout<<"NO\n";
}
}
}

B

题意:给出 $x,y,k$ ,一共操作 $k$ 次,每次操作先对 $x$ 加1,若 $x$ 是 $y$ 的倍数,那么对 $x$ 除以 $y$ 知道不能整除。求最后的 $x$ 。$x,y,k\leq 10^9$

一直对 $x$ 进行除操作,每次除的时候记录一下补了多少才能整除。最后当 $x$ 变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
40
41
42
43
44
45
46
47
48
#include<bits/stdc++.h>
using namespace std;

int main()
{
int t;
cin>>t;
while(t--)
{
int x,y,k;
cin>>x>>y>>k;

int e=y-x%y;
if(k<e)
{
cout<<x+k<<endl;
continue;
}
x=x/y+1;
k-=e;

int acc=0;
bool flag=0;
while(x!=1)
{
if(k<y-x%y && x%y!=0 )
{
cout<<x+k<<endl;
flag=1;
break;
}
if(x%y!=0 )k-=y-x%y;
x=(x-1)/y+1;

}

int ex=y-1;
k=k%ex;
if(!flag)
{
cout<<x+k<<endl;
}

}



}

math


C

有 $n$ 个数,每回合可以从左边依次拿走若干个数,若满足本回合拿走的数的和在区间 $[l,r]$ ,那么赢得一次。求最大获胜次数。

最开始用的贪心,属实小脑萎缩了太唐了

考虑 $dp$ ,设计状态 $dp[i]$ 表示最后一个取第 $i$ 个数时最大获胜次数。对每一个 $i$ ,找以 $i$ 结尾的一个最短区间 $[pos,i]$ 使得和在区间 $[l,r]$ 中,若存在,那么 $dp[i]=dp[pos-1]+1$ ,否则 $dp[i]=dp[i-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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[200005];
int dp[200005];
int pre[200005];
int n,l,r;

int find(int pos)
{
if(pre[pos]<l)return 0;
int ll=1,rr=pos;
while(ll<rr)
{
int mid=(ll+rr+1)>>1;
// cout<<"mid= "<<mid<<" ll="<<ll<<" rr="<<rr<<endl;
if(pre[pos]-pre[mid-1]>=l)ll=mid;
else rr=mid-1;


}
return ll;
}

void solve()
{
cin>>n>>l>>r;

for(int i=1;i<=n+2;i++)dp[i]=0,pre[i]=0;

for(int i=1;i<=n;i++)cin>>a[i],pre[i]=pre[i-1]+a[i];

int ans=0;
int t=0;
for(int i=1;i<=n;i++)
{
int x=find(i);
if(x==0)dp[i]=0;
else if(pre[i]-pre[x-1]>r)dp[i]=dp[i-1];
else dp[i]=dp[x-1]+1;
}

for(int i=1;i<=n;i++)ans=max(ans,dp[i]);
cout<<ans<<endl;

}

signed main()
{
int t;
cin>>t;
while(t--)solve();

return 0;
}

二分,dp


D

题意:给一个 $n\times m$ 的网格,每个格子有一个高度,同时所有格子被分为两类。每次可以对一个 $k\times k$ 的网格进行每个元素加 $c$ 的操作,$c$ 为自己决定。能否使有限次操作后,两类格子的和相等。

设 $tot=sum_1-sum_2$ ,那么在每个 $k\times k$ 的网格中,每次操作使答案变化 $c\times d_{0,1}$ ,即 $c$ 乘上网格中两类网格的数量差。因此可以得到最后的答案只与 $d$ 有关,那么可以处理出所有的 $d$ ,最后只需满足:
$$
c_1d_1+c_2d_2+…+c_{cnt}d_{cnt}=sum
$$
即判断 $sum$ 能否整除 $gcd(d_1,d_2,…d_{cnt})$。

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include<bits/stdc++.h>
using namespace std;
#define int long long
int mp[600][600];
int tp[600][600];
int w[260000];
int n,m,k;

int pre[600][600];

void solve()
{
cin>>n>>m>>k;
int tot=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>mp[i][j];
}
}

char c[600][600];
for(int i=1;i<=n;i++)cin>>c[i]+1;

for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{

if(c[i][j]=='1')tp[i][j]=1,tot+=mp[i][j];
else tp[i][j]=-1,tot-=mp[i][j];

}
}
if(tot==0)
{
cout<<"YES\n";
return ;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
pre[i][j]=tp[i][j]+pre[i][j-1]+pre[i-1][j]-pre[i-1][j-1];
}
}

// for(int i=1;i<=n;i++)
// {
// for(int j=1;j<=m;j++)
// {
// cout<<pre[i][j]<<" ";
// }cout<<endl;
// }

int cnt=0;
for(int i=k;i<=n;i++)
{
for(int j=k;j<=m;j++)
{
w[++cnt]=pre[i][j]-pre[i-k][j]-pre[i][j-k]+pre[i-k][j-k];
}
}

// for(int i=1;i<=cnt;i++)cout<<w[i]<<" ";

int gcd=w[1];
for(int i=1;i<=cnt;i++)gcd=__gcd(gcd,w[i]);
if(gcd==0)
{
cout<<"NO\n";
return ;
}
if(tot%gcd==0)cout<<"YES\n";
else cout<<"NO\n";

}

signed main()
{
int t;
cin>>t;
while(t--)solve();
// system("pause");
return 0;
}

math,二维前缀和