前言
期末周好几天忙复习都没写题,感觉什么都不会了呜呜TT
赛时牢骚:牛魔的舍友简直不是人,他妈从十点开始开麦打游戏,一边打一边吼。我带着降噪耳机音乐声开最大都听得一清二楚,吵得我脑子一团浆糊,真的草了......
一、A. Yes or Yes
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
void solve()
{
string s;
cin>>s;
int n=s.length();
s=" "+s;
int yes=0;
for(int i=1;i<=n;i++)
{
if(s[i]=='Y')
{
yes++;
}
}
if(yes>=2)
{
NO;
}
YES;
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
init();
while(t--)
{
solve();
}
return 0;
}
因为是或运算,所以过程中只要有一个Y,那么以后就都是Y了。那么当来到最后一步时,可以发现只要字符串中Y的个数大于等于两个,那么合并到最后一定剩下两个Y,此时就不合法了。所以如果想让合并的过程中不出现两个Y,那么就要求Y的个数最多只能有一个。
二、B. Impost or Sus
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
void solve()
{
string s;
cin>>s;
int n=s.length();
s=" "+s;
int pre=0;
int ans=0;
for(int i=1;i<n;i++)
{
if(s[i]=='u')
{
if(pre)
{
pre=0;
}
else
{
ans++;
pre=1;
}
}
else
{
pre=1;
}
}
if(s[n]=='u')
{
ans++;
}
cout<<ans<<endl;
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
init();
while(t--)
{
solve();
}
return 0;
}
byd这题翻译内鬼了还被硬控了一会儿......
看原文,意思就是要求对于每个u,距离其最近的两个s字符到其的距离相同,所以其实就是要求这两个s字符关于这个u字符对称。那么之后考虑往中间塞字符,因为两边距离最近的s字符已经确定,所以如果塞s字符的话就会破坏这个条件。而如果往里塞u的话,可以发现此时就必然不可能满足这两个s字符到其的距离相同。所以就可以得出,唯一合法的条件就是"sus"这样。所以之后按照这种方式判断是否需要改即可,注意如果最后的字符是u的话需要多改一次。
三、C. First or Second
这道题告诉我们有时候不要相信第一感觉和常识,还是需要就题分析......
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
//赛时傻逼解
void solve1()
{
int n;
cin>>n;
vector<ll>a(n+1);
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
vector<ll>dp(n+1);
dp[1]=a[1];
ll pre=dp[1];
for(int i=2;i<=n;i++)
{
dp[i]=pre+a[i];
if(dp[i]>pre)
{
pre=dp[i];
}
else
{
pre-=a[i];
}
}
vector<ll>suf(n+2);
for(int i=n;i>=1;i--)
{
suf[i]=suf[i+1]+a[i];
}
ll ans=-INFLL;
for(int i=1;i<=n;i++)
{
ans=max(ans,dp[i]-suf[i]);
}
cout<<ans<<endl;
}
//正解
void solve2()
{
int n;
cin>>n;
vector<ll>a(n+1);
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
//若选择让第i个人留下
//那么其后面的所有人只能作为第二个人被减去
//其前面的人除了第一个人必须加,剩下的人全部可以取得正贡献
//方法是可以考虑每次让正数作为第一个人
//先把到下一个正数前的所有负数减去,再选择当前正数
vector<ll>pre(n+1);
pre[1]=a[1];
for(int i=2;i<=n;i++)
{
pre[i]=pre[i-1]+abs(a[i]);
}
vector<ll>suf(n+2);
for(int i=n;i>=1;i--)
{
suf[i]=suf[i+1]+a[i];
}
ll ans=-INFLL;
for(int i=1;i<=n;i++)
{
ans=max(ans,pre[i-1]-suf[i+1]);
}
cout<<ans<<endl;
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
init();
while(t--)
{
solve2();
}
return 0;
}
赛时这个傻逼解就不说了,太有病了......
首先,我承认这个题第一眼确实是dp,但经过分析后需要发现dp做不了或者非常复杂。()之后,因为正着考虑会比较复杂,那么就考虑从最后开始分析。因为最后必然会剩下一个人,那么对于剩下的这个人,可以发现其右侧的人必然是全部作为第二个人减去的。而对于其左侧的人,首先可以发现第一个人必然是作为第一个人加上的。之后,因为想要结果最大,那么此时若存在若干个连续的负数,那么就都可以作为第二个人减去,从而取得正贡献。此时,第二个位置就是正数了,那么此时就可以加上第一个人,然后让这个正数作为第一个人,那么之后就可以取得正贡献了。所以以此类推,对于最后剩下的这个人的左侧,是全可以取到正贡献的。所以只需要维护一个前缀绝对值和一个后缀和,然后枚举哪个人最后剩下取最大值即可。
四、D. Xmas or Hysteria
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
void solve()
{
int n,m;
cin>>n>>m;
vector<array<ll,2>>a(n+1);
for(int i=1;i<=n;i++)
{
cin>>a[i][0];
a[i][1]=i;
}
if(m>n/2)
{
cout<<-1<<endl;
return ;
}
sort(a.begin()+1,a.end());
if(m==0)
{
vector<pii>ans;
ll sum=0;
int i=n-1;
while(i>=1&&sum+a[i][0]<a[n][0])
{
ans.push_back({a[i][1],a[n][1]});
sum+=a[i][0];
i--;
}
if(i==0)
{
cout<<-1<<endl;
return ;
}
for(int j=1;j<i;j++)
{
ans.push_back({a[j][1],a[j+1][1]});
}
ans.push_back({a[i][1],a[n][1]});
cout<<ans.size()<<endl;
for(auto [i,j]:ans)
{
cout<<i<<" "<<j<<endl;
}
return ;
}
vector<pii>ans;
for(int i=1;i<=n-2*m;i++)
{
ans.push_back({a[i][1],a[i+1][1]});
}
for(int i=n-m+1,j=n-2*m+1;i<=n;i++,j++)
{
ans.push_back({a[i][1],a[j][1]});
}
cout<<ans.size()<<endl;
for(auto [i,j]:ans)
{
cout<<i<<" "<<j<<endl;
}
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
init();
while(t--)
{
solve();
}
return 0;
}
因为每次攻击必然导致一只精灵死亡,一只精灵再也无法攻击。所以若想要m只精灵最后存活,那么就有n-m只精灵死亡。那么就必须进行n-m次攻击,同时会有n-m只精灵再也无法攻击。又因为需要保证存活下来的精灵全攻击过,所以如果想要存活的精灵最多,那么就是当n-m恰好等于m的时候,即存活下来的精灵全攻击过,所以若n小于2*m就不可能实现。
考虑对精灵按攻击力从小到大排序,那么肯定是攻击力最大的m个精灵更有可能活下来,所以考虑让最大的m个精灵攻击剩下的精灵。由于这样只能打死剩下的m个精灵,会有精灵没被攻击过。所以需要考虑先让剩下的精灵攻击到只剩m个,此时再让最大的m个精灵打一遍即可。对于剩下的精灵,可以考虑链式攻击,就是每次让小的去攻击大的,直到让剩下的精灵只剩m个。
特别地,若要求所有小精灵全死,首先肯定需要保证血量最厚的小精灵能被打死。所以可以考虑先从大到小让这些精灵都攻击最大的,那么若打到最后都没法把最大的打死,就不可能实现。否则,那么就找到最后一下打死最大值的小精灵i,然后对于其左侧的精灵,同样链式攻击。然后再让这个精灵攻击最大值,同归于尽即可。
五、E. Flatten or Concatenate
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
ll ask(int l,int r)
{
cout<<"? "<<l<<" "<<r<<endl;
ll sum;
cin>>sum;
return sum;
}
void solve()
{
int n;
cin>>n;
int L=1;
int R=n;
ll cur=ask(1,n);
while(L<R)
{
int l=L;
int r=R;
int m;
while(l<=r)
{
m=l+r>>1;
ll res=ask(L,m);
if(res==cur/2)
{
cur/=2;
if(m-L+1<=R-m)
{
R=m;
}
else
{
L=m+1;
}
break;
}
if(res>cur/2)
{
r=m-1;
}
else
{
l=m+1;
}
}
}
ll ans=ask(L,L);
cout<<"! "<<ans<<endl;
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
init();
while(t--)
{
solve();
}
return 0;
}
首先还是看操作次数,因为n很大而300次非常少,那么就需要考虑logn的做法。之后不难发现,不管怎么分裂,整个数组的累加和是不会变的。那么每拼接一次,整个数组的累加和就会变成原来的两倍,那么其实就可以用二分去找每次拼接的位置。
之后,因为操作一每次只会分裂当前数组内最大的元素,又因为每次分裂后都会使数组增加1长度,所以数组越长表明分裂的次数越多。又因为两数组初始是一样的,所以对于两个数组,长度更短的那个数组的最大值肯定不会小于长度更长的数组。那么当二分找到拼接的位置时,若左侧的长度更短,那么就说明最大值一定在左侧数组,否则就说明最大值一定在右侧数组。
所以整体思路就是二分套二分,外面的二分表示当前所在的范围,每次去内部二分找累加和一半位置,然后根据左右侧大小考虑之后去一侧继续二分,最后结束位置的就是整个数组的最大值。因为n是1e5的,那么二分的复杂度logn就是17,17的平方等于289,所以是可以的。
总结
先忙期末,忙完期末寒假开始加加加训!!