- 谢奇轩 的博客
七月暑期集训DAY02总结
- @ 2024-7-9 21:18:00
难度:⭐
思路
有2种情况:
(1)首尾相同,直接输出1。
(2)首尾不同,去找一个,满足&&,也就是第个数可以与第一个数一起消除第一段,第个数可以与第一个数一起消除第二段。
如果,还要特判一下。
代码
#include<bits/stdc++.h>
using namespace std;
int a[500005];
int n;
int cmp(){
if(n<=3){
if(n==1){
return -1;
}else{
if(a[1]!=a[n]){
return -1;
}
return 1;
}
}
if(a[1]==a[n]){
return 1;
}
for(int i=3;i<n;i++){
if(a[i]==a[n]&&a[i-1]==a[1]){
return 2;
}
}
return -1;
}
int main(){
freopen("num.in", "r", stdin);
freopen("num.out", "w", stdout);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
cout<<cmp();
return 0;
}
要点
- 虽然AC了,但是做题的过程中还是有点猴急,稳住心态最重要❤️
- 前几十分钟都没有想到从这个角度分类讨论,应该事先把讨论角度列出,然后分类讨论,有条不紊,效率更高📝
难度:⭐⭐
思路
有3种情况:
(1)&&,一山不容二虎,直接抵消,谁也活不了,结果为0。
(2),可以抵消所有气球,只有是最后的赢家,结果为1.
(3)大家差别不大,实力相当,怎么办?列草稿啊!
第一组
先看,剩下的气球可以分成三组:气球们自相残杀互相抵消,在旁边吃瓜这组数据中可能是剩下的。同理。结果为4.
第二组
先看,剩下的气球不管是各杀各的互相抵消,还是强强联手,对抗a5气球一起抵消,都会剩下至少一个,活不了,也一样。
再看,剩下的气球强强联手,对抗a5气球一起抵消,会剩下一个气球,再来抵消这一个,
a[3]得以幸存,比大,也一样能活。结果为2。
第三组
先看,剩下的气球先让和抵消,剩下一个。和抵消,剩下一个,然后和刚才的一个抵消。可能会剩下。其他的都比大,也可以,结果为5。
- 这里有一个问题出现了:同样是1,为什么第二组不能留下,而第三组可以?
我们来比较一下,似乎每一组最优抵消都只会剩下一个或者不剩。我们再来分类讨论,分别找一找两类结果的抵消数据的共同点:
抵消结果为1的数据:
抵消结果为0的数据:$\{2,2,2\},\{1,1,1,3\},\{1,3,4,5\},\{1,2,4,5\},\{1,2,3,4\}$
发现了吗?抵消结果是与数据和对2的取模。
那我们就可以从奇偶性入手,由此可得:
若的总和是奇数,则有:
设的总和为。
设为奇数,那么为偶数,抵消结果是0。
设为偶数,那么为奇数,抵消结果是1。
这道题输入最小的偶数是,所以若的总和是奇数,结果为。
反之,则有:
设的总和为。
设为偶数,那么为偶数,抵消结果是0。
设为奇数,那么为奇数,抵消结果是1。
这道题输入最小的奇数是,直接蒸发,所以还要有统计大于一的数的个数。若的总和是偶数,结果为。
代码
#include<bits/stdc++.h>
using namespace std;
long long a[100005];
int main(){
freopen("balloon.in", "r", stdin);
freopen("balloon.out", "w", stdout);
long long n,mx=0,cnt=0;
cin>>n;
long long s=0;
for(int i=1;i<=n;i++){
cin>>a[i];
s+=a[i];
mx=max(mx,a[i]);
if(a[i]>1){
cnt++;
}
}
if(n==2&&a[1]==a[2]){
cout<<0;
}else if(mx>=s-mx){
cout<<1;
}else if(s%2==1){
cout<<n;
}else{
cout<<cnt;
}
return 0;
}
要点
- 虽然拿部分分了,但是做题的过程中还是没有想到第一点和第二点,第三点也没有深入想。思考的时候,应该向DFS学习:“一个点想到底,想完再想其他角度”🧐
难度:⭐
思路
及其淼的一道题,用贪心思想,拿大的数减一,拿小的数加一。
代码
#include<bits/stdc++.h>
using namespace std;
int a[5];
string ans;
int main(){
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
int n,f=1;
cin>>n>>a[0]>>a[1]>>a[2];
while(n--){
string op;
cin>>op;
int x=op[0]-'A',y=op[1]-'A';
if(a[x]>a[y]){
a[x]--;
a[y]++;
ans+=op[1];
}else{
a[y]--;
a[x]++;
ans+=op[0];
}
if(a[x]==-1||a[y]==-1){
f=0;
}
}
if(f==0){
cout<<"No";
}else{
cout<<"Yes"<<endl;
for(int i=0;i<ans.size();i++){
cout<<ans[i]<<endl;
}
}
return 0;
}
要点
- 虽然AC了,但是做题的过程中还是想复杂了,应该尽量简单📏
难度:⭐
思路
有2种思路:
(1)数组标记法。根据福尔摩斯的名言:排除所有不可能之后剩下的那个就是真相。我们把所有可以确定不是仙峰台的观景台都标记起来,剩下的就是仙峰台。
(2)邻接表遍历法。把图用邻接表储存起来,每一个观景台单独遍历与其相连的观景台,比较大小,每一个都执行一次,就可以找到所有仙峰台。
代码
(数组标记法)
#include<bits/stdc++.h>
using namespace std;
int a[100005];
int vis[100005];
int main(){
freopen("penglai.in", "r", stdin);
freopen("penglai.out", "w", stdout);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
while(m--){
int x,y;
cin>>x>>y;
if(a[x]<a[y]){
vis[x]=1;
}else if(a[y]<a[x]){
vis[y]=1;
}else{
vis[x]=1;
vis[y]=1;
}
}
int sum=0;
for(int i=1;i<=n;i++){
if(vis[i]==0){
sum++;
}
}
cout<<sum;
return 0;
}
(邻接表遍历法)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5, M = N*2;
int h[N],e[M],ne[M],idx,n,m,st[N];
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx;
idx++;
}
int a[N];
bool cmp(int i){
if(h[i]==-1){
return 1;
}
for(int j=h[i];j!=-1;j=ne[j]) {
if(a[i]<=a[e[j]]){
return 0;
}
}
return 1;
}
int main() {
freopen("penglai.in", "r", stdin);
freopen("penglai.out", "w", stdout);
cin >> n>>m;
memset(h, -1, sizeof h);
for (int i=1; i<=n; i++) {
cin>>a[i];
}
for (int i=1; i<=m; i++) {
int x,y;
cin >>x>> y;
add(x,y);
add(y,x);
}
int ans=0;
for (int i=1;i<=n; i++) {
ans+=cmp(i);
}
cout<<ans;
return 0;
}
要点
- 虽然AC了,但是做题的过程中没有想到邻接表遍历法,应该多想📄
难度:⭐⭐⭐
思路
是的倍数
是的倍数
循环查找即可。
代码
#include<bits/stdc++.h>
using namespace std;
int main(){
freopen("math1.in", "r", stdin);
freopen("math1.out", "w", stdout);
int n,m,l=0;
cin>>n>>m;
for(long long b=1;b<=m;b++){
for(long long a=b;a<=n;a+=b){
if((a+b)%(b*b)==0){
l++;
}
}
}
cout<<l;
return 0;
}
要点
- 看到题目只想到了找规律,没有想到推公式➗
难度:⭐⭐⭐
思路
纯暴搜必定超时,所以需要DP来解。
从一个小木屋到另一个小木屋有多少条路呢?(不考虑长短)当然是条,那不可以走的有多少条路?当然是条。那么可以走的路线就是条,用数组来统计长短路线和,那就是条。
开一个f数组,表示第天在第个小木屋有多少种方案,假设第天在第个小木屋,天在第个小木屋,那么就有种方案,由于前一天并不确定在哪一个小木屋,所以要循环求和。
算的过程中可能会数据溢出,应该在各个地方加上取模处理。
代码
#include<bits/stdc++.h>
using namespace std;
const int Mod=1e9+7;
long long l[110],s[110],r[110],f[1010][110];
int main(){
freopen("walk.in", "r", stdin);
freopen("walk.out", "w", stdout);
int n,m;
cin>>m>>n;
for(int i=1;i<=m;i++){
cin>>s[i];
r[i]=s[i];
}
for(int i=1;i<=m;i++){
cin>>l[i];
r[i]+=l[i];
}
f[0][1]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
long long sum=0;
for(int k=1;k<=m;k++){
sum+=(((r[j]*r[k]-l[j]*l[k])%Mod)*f[i-1][k]%Mod)%Mod;
sum%=Mod;
}
f[i][j]=sum;
}
}
int sum=0;
for(int i=1;i<=m;i++){
sum=(sum%Mod+f[n][i])%Mod;
}
cout<<sum;
return 0;
}
要点
- 做的时候看到直接放弃,但复现赛写的时候却一下就过了,思路也很简单,骗分也很容易,不应该看一眼就放弃🙋♂️