A. Yogurt Sale
贪心
大概思路
两种买法,哪种最省就用哪种买,奇数情况要小判一下
参考代码
void solve(){
cin(n);cin(a);cin(b);
if(1.0*b/2>a)cout<<a*n<<et;
else cout<<(n/2)*b+(n%2)*a<<et;
}
B. Progressive Square
暴力,构造
大概思路
由题意知给定参数下的矩阵唯一,直接以元素最小值,按题意生成矩阵元素,然后暴力判是否相等即可
参考代码
void solve(){
cin(n);cin(c);cin(d);
//记录矩阵元素
vector<int>a(n*n),b;
CV(a);
sort(a.begin(),a.end());
//暴力生成元素
rep(i,0,n-1)rep(j,0,n-1)b.push_back(a[0]+i*c+j*d);
sort(b.begin(),b.end());
ANS(a==b);
}
C. Inhabitant of the Deep Sea
暴力,模拟
大概思路
直接取一半,两边往中间暴力删数即可
参考代码
void solve(){
cin(n);cin(k);
vector<ll>a(n);
CV(a);
ll sum=0;
for(auto &i:a)sum+=i;
if(k>=sum){cout<<n<<et;return;}
ll l=k/2+k%2;
ll r=k/2;
int cnt=0;
rep(i,0,n-1){
if(l<a[i]){a[i]-=l;break;}
l-=a[i];a[i]=0;cnt++;
}
rrep(i,n-1,0){
if(r<a[i])break;
r-=a[i];cnt++;
}
cout<<cnt<<et;
}
D. Inaccurate Subsequence Search
滑动窗口
大概思路
用小的数组在大数组上滑动统计答案即可,开个map桶记录出现的数个数
参考代码
void solve(){
cin(n);cin(m);cin(k);
vector<int>a(n);
CV(a);
map<int,int> cnt,acnt;
rep(i,1,m){//记录小数组(匹配用)的各元素出现次数
cin(tmp);
cnt[tmp]++;
}
ll ans=0;
rep(i,0,m-1){//将小数组与大数组左端对其,收集子数组出现的元素个数
acnt[a[i]]++;
}
ll same=0;//先暴力统计一遍匹配的数,准备开始滑动
for(auto &[i,v]:acnt){
if(acnt[i]&&cnt[i])same+=min(acnt[i],cnt[i]);
}
if(same>=k)ans++;
rep(i,m,n-1){//开始滑动,边滑动边收集
if(acnt[a[i-m]]<=cnt[a[i-m]])same--;
acnt[a[i-m]]--;
if(acnt[a[i]]<cnt[a[i]])same++;
acnt[a[i]]++;
if(same>=k)ans++;
}
cout<<ans<<et;
}
E. Long Inversions
贪心,差分
大概思路
会的人一下子就想出来了,不会的怎么想都想不出来
从左到右贪心,比方说1011101这个样例,设k=4,如果只对某一位取反,必然会影响到第i+k位的数,在k等于4时,是否该取反的状态如下:
第1到第4个数:0100 第5到第7个数:010
此时按位异或,会发现他们均为0
- 对样例0100010,取k=4时,取反状态如下:
[1~4]:1011 [5~7]:101
按位异或不全为0,故k不可能取4;取k=3时,有:
[1~3]:101 [4~6]:110 [7~7]:1
按位异或全为1,其一种推导路径如下:
0100010 0111110 1001110 1001001 1110001 1111111
因此我们只需要开个差分数组,暴力从大到小枚举k,找到使差分数组值全相等时的情况输出即可(k=1必然可行,不用担心边界)
参考代码
void solve(){
cin(k);scin(s);
do{
vector<int>a(k);
rep(i,0,s.length()-1)a[i%k]^='1'-s[i];
if(a==vector<int>(k,a[0])){cout<<k<<et;return;}
}while(k--);
}
F. Unfair Game
位运算,结论题
大概思路
水题,把1,2,3,4二进制状态全列出来如下:
001 010 011 100
很容易发现1^2^3=0的结论,又因为偶数个相同的数异或起来为0,所以设1,2,3,4的个数分别为a,b,c,d的话,答案就是a/2+b/2+c/2+d/2,当a,b,c均为奇数时,答案要加1
参考代码
void solve(){
cin(a);cin(b);cin(c);cin(d);
cout<<a/2+b/2+c/2+d/2+(a%2&&b%2&&c%2)<<et;
}
G. GCD on a grid
因数,bfs,可行性DP(感觉能用可行性dp的,一般也能bfs)
大概思路
hack期最有趣的一题,有人拿了逆天数据hack掉了300多号人,排名第一页全军覆没,还好赛后重测时又重测了(不对,我又没ac,我管这么多干嘛)
从2到 $\gcd(a[0][0],a[n][m])$ 枚举因子,然后跑bfs或dp即可(似曾相识的思路)
题外话,开vector跑了1000ms,开全局数组只跑了500ms,快了一倍
参考代码
void solve() {
cin(n);cin(m);
vectors<2>a(n,vector<int>(m)),dp(n,vector<int>(m));
rep(i,0,n-1)rep(j,0,m-1)cin>>a[i][j];
auto ck=[&](int d){
dp.assign(n, vector<int>(m, 0));
rep(i,0,n-1)rep(j,0,m-1)dp[i][j]=0;
dp[0][0]=1;//可行性dp
rep(i,0,n-1)rep(j,0,m-1)if(a[i][j]%d==0) {
if(i)dp[i][j]|=dp[i-1][j];//可行性传递
if(j)dp[i][j]|=dp[i][j-1];
}
return dp[n-1][m-1];
};
int qwq=__gcd(a[0][0],a[n-1][m-1]),ans=1;
for(int d=1;d<=qwq/d;d++){
if(qwq%d==0){
if(ck(d))ans=max(ans,d);
if(ck(qwq/d)){ans=max(ans,qwq/d);break;}
}
}
CA;
}
H. The Most Reckless Defense
费用流,MCFC,状压dp
大概思路
没学过费用流和MCFC,这里只看了状压dp的思路
题意说,每个半径只能使用一次,怪的血量又与半径有关系,这个关系式中含指数,半径每增大1,可对怪造成的最大影响就增大 $\Delta D= (r^2-(r-1)^2)\times 500$,但是怪的额外血量会提高 $\Delta H=3^r-3^{r-1}$,算一下会发现,当 $r=10$ 时,$\Delta H>\Delta D$,因此半径再继续增加是没有意义的。
所以我们设10个状态位,表示1到10的半径使用情况,做状压dp。设$dp[S]$ 表示半径选择状态为 S 时,可以设置的最大基础血量,然后每读入一个防御塔信息后,枚举状态,枚举该防御塔的半径,更新各个状态的最优值,最后输出 $dp[(1«10)-1]$ 这个状态即可(半径全用上肯定是最优的)。
参考代码
void solve() {
ll n,m,k;cin>>n>>m>>k;
string s[n+1];
rep(i,1,n){cin>>s[i];s[i]=" "+s[i];}
vector<int> dp(1<<10),dp_(1<<10);
while(k--){
ll x,y,p;cin>>x>>y>>p;
ll cnt[11]={0};//统计该塔在半径设为r时,可影响到的格子数
rep(i,1,n)rep(j,1,m)if(s[i][j]=='#'){
int r=ceil(hypot(x-i,y-j));
if(r<=10)cnt[r]++;
}
rep(i,1,10)cnt[i]+=cnt[i-1];
dp_=dp;//滚动一下
rep(I,0,(1<<10)-1){//枚举所有状态
int w=1;
rep(j,1,10){//枚举该塔取各个半径r时,当前状态是否可以有更优的答案
w*=3;
if(I>>j-1&1)dp[I]=max(dp[I],dp_[I^1<<j-1]+cnt[j]*p-w);
}
}
}
cout<<dp[(1<<10)-1]<<et;
}
附录
这里贴上用到的头部和main函数(有更新,更了奇怪的类模版递推以简化嵌套vector的写法):
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using ld = long double;
namespace Xiaoxia{
#include<iostream>
#include<vector>
#include<cmath>
#include<string>
#include<algorithm>
#define et '\n'
#define reg register
#define il inline
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define rrep(i,a,b) for(int i=(a);i>=(b);i--)
#define cin(a) ll a;cin>>a
#define dcin(a) ld a;cin>>a
#define scin(a) string a;cin>>a
#define CA cout<<ans<<et
#define CY cout<<"YES"<<et
#define CN cout<<"NO"<<et
#define max(a,b) ((a>b)?(a):(b))
#define min(a,b) ((a<b)?(a):(b))
#define CV(x) for(auto &i:x)cin>>i
template<int n,typename T>struct VEC_ {
using type=vector<typename VEC_<n-1,T>::type>;
};
template<typename T>struct VEC_<1,T> {
using type=std::vector<T>;
};
template <int n=1,typename T=int>
using vectors = typename VEC_<n, T>::type;
//#define PP(l,r,CK) *ranges::partition_point(ranges::iota_view((l),(r)+1),(CK))
int M = 1e9 + 7;
inline ll MO(ll x){return (x%M+M)%M;}
inline void ANS(bool x){cout<<(x?"Yes\n":"No\n");}
}
using namespace Xiaoxia;
void solve(){
}
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
//pre();
cin(t);while(t--)
solve();
return 0;
}