前言
牛魔的D没看到L的限制卡了半天,把E秒了回来才过的T^T
一、A - Black Square
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 p,q,x,y;
cin>>p>>q>>x>>y;
if(p<=x&&x<=p+100-1&&q<=y&&y<=q+100-1)
{
Yes;
}
No;
}
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;
}
没啥好说的,判断一下边界输出即可,注意边界要 -1 即可。
二、B - Two Languages
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;
string s,t;
cin>>s>>t;
s=" "+s;
t=" "+t;
set<char>a,b;
for(int i=1;i<=n;i++)
{
a.insert(s[i]);
}
for(int i=1;i<=m;i++)
{
b.insert(t[i]);
}
int q;
cin>>q;
string w;
while(q--)
{
cin>>w;
bool A=true,B=true;
for(auto c:w)
{
if(a.find(c)==a.end())
{
A=false;
}
if(b.find(c)==b.end())
{
B=false;
}
}
if(A^B)
{
if(A)
{
cout<<"Takahashi"<<endl;
}
else
{
cout<<"Aoki"<<endl;
}
}
else
{
cout<<"Unknown"<<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;
}
还是经典 abc 的 stl 题,就是把两串出现的字符全存set里,然后对于每个字符串,考察是否全是A里的字符或全是B里的字符。若只有其中一个满足,即异或为 true,那么就可以判断出来输出,否则就是Unknown。
三、C - Sake or Water
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()
{
ll n,k,x;
cin>>n>>k>>x;
vector<ll>a(n+1);
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a.begin()+1,a.end());
ll sum=0;
for(int i=k;i>=1;i--)
{
sum+=a[i];
if(sum>=x)
{
cout<<k-i+1+n-k<<endl;
return ;
}
}
cout<<-1<<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;
}
上来没贪对还WA了一发......
首先,如果想让喝的次数最少,那么肯定是先喝容量大的。之后,因为想要让喝的杯数尽可能地多,那么肯定是让容量最大的n-k个杯子里全是水。所以在从小到大排序后,前k个杯子就是有酒的杯子。那么从第k个杯子开始从大往小枚举,当碰到累加和大于等于 x 的第一个位置就可以输出了,否则就说明没法喝满 x 。
四、D - Paid Walk
没看到L的限制卡了半天,过了E回来才发现......
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,l,s,t;
cin>>n>>m>>l>>s>>t;
vector<vector<pll>>g(n+1);
for(int i=1,u,v,w;i<=m;i++)
{
cin>>u>>v>>w;
g[u].push_back({v,w});
}
vector<vector<vector<ll>>>dp(n+1,vector<vector<ll>>(l+1));
auto dfs=[&](auto &&self,int u,int step,int cur)->void
{
if(step==l)
{
return ;
}
for(auto [v,w]:g[u])
{
dp[v][step+1].push_back(cur+w);
self(self,v,step+1,cur+w);
}
};
dp[1][0].push_back(0);
dfs(dfs,1,0,0);
vector<int>ans;
for(int i=1;i<=n;i++)
{
for(auto x:dp[i][l])
{
if(s<=x&&x<=t)
{
ans.push_back(i);
break;
}
}
}
for(auto x:ans)
{
cout<<x<<" ";
}
}
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;
}
注意到L的范围很小,那么就可以直接暴力定义dp[i][j]为从1号节点到 i 号节点所有长度为 j 的路径的权值和。
那么之后就可以直接带着当前节点u,之前走过的长度step和当前权值和去dfs,每次考察孩子节点,往dp[v][step+1]里push_back。最后考察每个节点长度为L的路径中所有的权值和,只要存在一个权值和在范围[S,T]内的路径,当前节点就可以加入答案。
额外的,因为说明了每个节点的外度最多为4,所以从 1 节点出发走过长度为L的路径的数量最多只有4L条,因为L很小,所以这个枚举的复杂度是可以接受的。
五、E - A > B substring
有了板子就是爽()
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};
template<typename T>
struct BIT
{
vector<T>tree;
BIT(int n)
{
tree.resize(n);
}
int lowbit(int i)
{
return i&-i;
}
void add(int i,T v)
{
while(i<tree.size())
{
tree[i]+=v;
i+=lowbit(i);
}
}
T sum(int i)
{
T ans=0;
while(i>0)
{
ans+=tree[i];
i-=lowbit(i);
}
return ans;
}
T query(int l,int r)
{
return sum(r)-sum(l-1);
}
};
void solve()
{
int n;
cin>>n;
string s;
cin>>s;
s=" "+s;
BIT<int>tree(2*n+2);
int A=0,B=0;
tree.add(A-B+n+1,1);
ll ans=0;
for(int i=1;i<=n;i++)
{
if(s[i]=='A')
{
A++;
}
if(s[i]=='B')
{
B++;
}
ans+=tree.sum(A-B+n);
tree.add(A-B+n+1,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--)
{
solve();
}
return 0;
}
对于区间内A的数量大于B的数量的这个条件,还是考虑将区间问题转化成前缀问题。那么对于当前位置,若前缀A有x个,前缀B有y个,若前面某位置的前缀A有x'个,B有y'个,那么若想要这个区间符合要求,就需要满足x-x'大于y-y',移项可得x-y大于x'-y'。
所以当前位置能产生的贡献,就是前缀x-y小于当前x-y的个数,那么这个就可以考虑使用树状数组维护了。由于x-y会出现负数,那么就需要将其加上n+1偏移一下,每次统计完当前的前缀个数A和B后,计算对答案的贡献,然后更新树状数组即可。
六、F - Must Buy
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++)
{
//p v
cin>>a[i][0]>>a[i][1];
}
vector<vector<ll>>pre(n+1,vector<ll>(m+1));
pre[0][0]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
pre[i][j]=pre[i-1][j];
if(j-a[i][0]>=0)
{
pre[i][j]=max(pre[i][j],pre[i-1][j-a[i][0]]+a[i][1]);
}
}
}
vector<vector<ll>>suf(n+2,vector<ll>(m+1));
suf[n+1][0]=0;
for(int i=n;i>=1;i--)
{
for(int j=m;j>=0;j--)
{
suf[i][j]=suf[i+1][j];
if(j-a[i][0]>=0)
{
suf[i][j]=max(suf[i][j],suf[i+1][j-a[i][0]]+a[i][1]);
}
}
}
ll ans=0;
for(int j=0;j<=m;j++)
{
ans=max(ans,pre[n][j]);
}
for(int i=1;i<=n;i++)
{
ll no=0;
for(int j=0;j<=m;j++)
{
ll p1=pre[i-1][j];
ll p2=suf[i+1][m-j];
no=max(no,p1+p2);
}
ll yes=0;
ll rest=m-a[i][0];
for(int j=0;j<=rest;j++)
{
ll p1=pre[i-1][j];
ll p2=suf[i+1][rest-j];
yes=max(yes,p1+p2);
}
yes+=a[i][1];
if(yes<ans)
{
cout<<"C";
}
else if(no<ans)
{
cout<<"A";
}
else
{
cout<<"B";
}
}
}
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;
}
首先,对于某一个商品,若选它的时候达不到最大值,那么这个商品就必然不能选。否则,即选它的时候能达到最大值,而不选的时候达不到最大值,那么这个商品就是必须选的。否则,即选不选都能达到最大值,那么就说明这个商品可选可不选。
之后,也算是一个常见的trick吧(?,就是对于需要单独讨论某个物品的问题,可以考虑构建前后缀,然后整合看最值。
所以在这个题里,可以考虑先正着跑一遍01背包构建出pre[i][j],再倒着跑一遍01背包构建出suf[i][j]。之后抓出最大值后,枚举每个物品,考虑要和不要两种情况的答案。
首先,对于不要的情况,可以枚举前缀选出的重量 j ,那么答案就是pre[i-1][j]+suf[i+1][m-j]的最大值。之后,对于要的情况,首先剩下可供选择的重量就是m-a[i][0]。然后就可以同样枚举前缀选的重量 j ,求出最大值,然后加上当前物品的价值,最后比较判断即可。
总结
保持好状态,多写题多积累,加油!!