A.The Fool / 愚者
题意:给定 \(n\) 行、每行 \(m\) 个连续的字符串,每个字符串长度为 \(k\) ,当中有且仅有一个与其他的字符串不同,找出这个字符串,输出它所在的行和列。
限制条件: \(n,m≤200, k≤10\)。
题解:直接模拟即可,时间复杂度\(O(nmk)\)。
码风这一块
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int main()
{ios::sync_with_stdio(0);cin.tie(0);ll tttt=1;//cin>>tttt;while(tttt--){ll n,m,k;cin>>n>>m>>k;map<string,ll>mp;vector<vector<string>>a(n+7,vector<string>(m+7));for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){string s="";for(int l=1;l<=k;l++){char c;cin>>c;s+=c;}a[i][j]=s;mp[s]++;}}for(auto [c,b]:mp){if(b==1){for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(a[i][j]==c){cout<<i<<' '<<j<<'\n';return 0;}}}}}}return 0;
}
J.Temperance / 节制
题意:给定三维平面内的 \(n\) 个点,每个点的密度定义为 \(max(a,b,c)\) ,其中 \(a,b,c\) 分别为除了该点以外与该点拥有相同的 \(x,y,z\) 坐标的点的个数。对于 \(k=0,1,···,n-1\) ,求出至少要移除多少点才能使剩下来的所有点的密度都大于等于 \(k\) 。
限制条件: \(n,|x|,|y|,|z|≤10^5\)。
题解:对于一个密度为 \(k_i\) 的点 \(i\) ,不妨设与它的 \(x\) 相等的点最多,即为 \(k_i\)。那么这些点在询问的 \(k≤k_i\) 时是不会被删去的,因为它们的密度至少也会是 \(k_i\)。故 \(i\) 号点只会在 \(k>k_i\) 的时候被删掉。对于每个点,处理出它的密度,然后把它在第几天被删掉的贡献加上,就能够得到 \(ans\) 数组,然后再依次求前缀和即可,时间复杂度 \(O(n)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
struct node
{ll x,y,z;
};
int main()
{ios::sync_with_stdio(0);cin.tie(0);ll tttt=1;cin>>tttt;while(tttt--){ll n;cin>>n;vector<node>a(n+7);vector<ll>dx(1e5+7,0),dy(1e5+7,0),dz(1e5+7,0),p(n+7),ans(n+7,0);for(int i=1;i<=n;i++){ll x,y,z;cin>>x>>y>>z;a[i]={x,y,z};dx[x]++;dy[y]++;dz[z]++;}for(int i=1;i<=n;i++){auto [x,y,z]=a[i];ans[max(max(dx[x],dy[y]),dz[z])+1]++;}for(int i=1;i<=n;i++){ans[i]+=ans[i-1];cout<<ans[i]<<" \n"[i==n];}}return 0;
}
B.The Magician / 魔术师
题意:给定了 \(n\) 张扑克牌,另外还有六种功能牌,你可能拥有其中的某些,每种最多一张。功能牌的功能分别是将三张牌变成方片、梅花、红桃、黑桃;将一张牌变为万能牌;将一张牌变成另一张牌的复制品,问最多能用这些牌打出几个同花,同花指的是五张相同花色的牌。
限制条件:给定的牌是一副扑克牌(无大小王)的子集,每种功能牌不超过一张。
题解:每种花色的牌只有不超过13张,算上所有功能牌也只能到18张,因此每种花色最多能打出3个同花。四种花色共有 \(4^4=256\) 种情况,分别枚举所有情况判断是否有可能组成同花即可。每次先统计一下每种花色在先不用功能牌的情况下组成要求的同花数量是否能剩下来牌,然后用所有剩下来的牌去填充缺少的数量即可。注意一下前四种功能牌的使用优先级是要高于后两种的,因为只对相应的花色有效。时间复杂度基本上是常数(
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int main()
{ios::sync_with_stdio(0);cin.tie(0);ll tttt=1;cin>>tttt;while(tttt--){ll n;cin>>n;vector<ll>num(5,0);for(int i=1;i<=n;i++){string s;cin>>s;if(s[1]=='D')num[1]++;else if(s[1]=='C')num[2]++;else if(s[1]=='H')num[3]++;else if(s[1]=='S')num[4]++;}ll t[7],ans=0;cin>>t[1]>>t[2]>>t[3]>>t[4]>>t[5]>>t[6];for(int i=0;i<256;i++){ll g[5]={0,i/64,i%64/16,i%16/4,i%4},mor=0,duo=t[5]+t[6];bool yw[5]={0,0,0,0,0};for(int i=1;i<=4;i++){mor+=max(0ll,num[i]-g[i]*5);}for(int i=1;i<=4;i++){if(num[i]<g[i]*5){if(g[i]*5-num[i]>5)yw[i]=0;else if(g[i]*5-num[i]==5){if(mor>=5&&t[i]&&duo>=2){yw[i]=1;mor-=5;duo-=2;}else yw[i]=0;}else if(g[i]*5-num[i]==4){if(mor>=4&&t[i]&&duo>=1){yw[i]=1;mor-=4;duo-=1;}else yw[i]=0;}else if(g[i]*5-num[i]==3){if(mor>=3&&t[i]){yw[i]=1;mor-=3;}else yw[i]=0;}else if(g[i]*5-num[i]==2){if(mor>=2&&t[i]){yw[i]=1;mor-=2;}else if(mor>=2&&duo>=2){yw[i]=1;mor-=2;duo-=2;}else yw[i]=0;}else if(g[i]*5-num[i]==1){if(mor>=1&&t[i]){yw[i]=1;mor-=1;}else if(mor>=1&&duo>=1){yw[i]=1;mor-=1;duo-=1;}else yw[i]=0;}}else yw[i]=1;}bool ok=1;for(int i=1;i<=4;i++){ok&=yw[i];}if(ok)ans=max(ans,g[1]+g[2]+g[3]+g[4]);}cout<<ans<<'\n';}return 0;
}
E.The Chariot / 战车
题意:一种出租车的定价方案为:X米内收起步价A元;X米到X+Y米内的部分每米收B元;超出X+Y米的部分每米收C元。你可以在任意位置重新打车,要去距离为D的地方,问最少花多少钱。
限制条件:\(0<A,B,C,D,X,Y<10^{2077}\)。 (意思就是在Python的计算范围内)
题解:首先我们明确一个共识,你要用一种策略就尽可能多用,你用一会这种策略用一会那种策略是不会比只用一种策略更优的。
按照 \(D\) 的大小分成三种情况:
- \(D≤X\) ,一个 \(A\) 秒了。
- \(X<D≤X+Y\),此时可能是一个 \(AB\);或者尽量用 \(A\) ,最后接一个 \(A\) 或者 \(B\)。
注意 \(B\) 和 \(C\) 是每米的价格,和 \(A\) 不一样。 - \(D>X+Y\),此时可能是一个 \(ABC\) ;或者尽量用 \(A\) ,最后接一个 \(A\) 或者 \(B\);或者尽量用 \(AB\) ,最后一部分可以是新开一个 \(A\),那么之前的一部分 \(B\) 就可以减少到让最后一部分刚好和 \(A\) 相等;也可以是新开一个 \(AB\),注意这里的 \(B\) 也不是算满的;或者是最后一个 \(AB\)后面用 \(C\) 填满。
通过造点样例观察就可以发现这些就包括了所有情况,其实就是从 \(A,AB,ABC\) 三种策略里面一直坚持一个。然后直接用Pytho写或者套高精度板子就行了。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
struct bigint {int sign;std::vector<int> v;bigint() : sign(1) {}bigint(const std::string &s) { *this = s; }bigint(int v) {char buf[21];sprintf(buf, "%d", v);*this = buf;}void zip(int unzip) {if (unzip == 0) {for (int i = 0; i < (int)v.size(); i++)v[i] = get_pos(i * 4) + get_pos(i * 4 + 1) * 10 +get_pos(i * 4 + 2) * 100 + get_pos(i * 4 + 3) * 1000;} elsefor (int i = (v.resize(v.size() * 4), (int)v.size() - 1), a; i >= 0; i--)a = (i % 4 >= 2) ? v[i / 4] / 100 : v[i / 4] % 100,v[i] = (i & 1) ? a / 10 : a % 10;setsign(1, 1);}int get_pos(unsigned pos) const { return pos >= v.size() ? 0 : v[pos]; }bigint &setsign(int newsign, int rev) {for (int i = (int)v.size() - 1; i > 0 && v[i] == 0; i--)v.erase(v.begin() + i);sign = (v.size() == 0 || (v.size() == 1 && v[0] == 0))? 1: (rev ? newsign * sign : newsign);return *this;}std::string to_str() const {bigint b = *this;std::string s;for (int i = (b.zip(1), 0); i < (int)b.v.size(); ++i)s += char(*(b.v.rbegin() + i) + '0');return (sign < 0 ? "-" : "") + (s.empty() ? std::string("0") : s);}bool absless(const bigint &b) const {if (v.size() != b.v.size()) return v.size() < b.v.size();for (int i = (int)v.size() - 1; i >= 0; i--)if (v[i] != b.v[i]) return v[i] < b.v[i];return false;}bigint operator-() const {bigint c = *this;c.sign = (v.size() > 1 || v[0]) ? -c.sign : 1;return c;}bigint &operator=(const std::string &s) {if (s[0] == '-')*this = s.substr(1);else {for (int i = (v.clear(), 0); i < (int)s.size(); ++i)v.push_back(*(s.rbegin() + i) - '0');zip(0);}return setsign(s[0] == '-' ? -1 : 1, sign = 1);}bool operator<(const bigint &b) const {return sign != b.sign ? sign < b.sign: (sign == 1 ? absless(b) : b.absless(*this));}bool operator==(const bigint &b) const { return v == b.v && sign == b.sign; }bigint &operator+=(const bigint &b) {if (sign != b.sign) return *this = (*this) - -b;v.resize(std::max(v.size(), b.v.size()) + 1);for (int i = 0, carry = 0; i < (int)b.v.size() || carry; i++) {carry += v[i] + b.get_pos(i);v[i] = carry % 10000, carry /= 10000;}return setsign(sign, 0);}bigint operator+(const bigint &b) const {bigint c = *this;return c += b;}void add_mul(const bigint &b, int mul) {v.resize(std::max(v.size(), b.v.size()) + 2);for (int i = 0, carry = 0; i < (int)b.v.size() || carry; i++) {carry += v[i] + b.get_pos(i) * mul;v[i] = carry % 10000, carry /= 10000;}}bigint operator-(const bigint &b) const {if (b.v.empty() || b.v.size() == 1 && b.v[0] == 0) return *this;if (sign != b.sign) return (*this) + -b;if (absless(b)) return -(b - *this);bigint c;for (int i = 0, borrow = 0; i < (int)v.size(); i++) {borrow += v[i] - b.get_pos(i);c.v.push_back(borrow);c.v.back() -= 10000 * (borrow >>= 31);}return c.setsign(sign, 0);}bigint operator*(const bigint &b) const {if (b < *this) return b * *this;bigint c, d = b;for (int i = 0; i < (int)v.size(); i++, d.v.insert(d.v.begin(), 0))c.add_mul(d, v[i]);return c.setsign(sign * b.sign, 0);}bigint operator/(const bigint &b) const {bigint c, d;bigint e = b;e.sign = 1;d.v.resize(v.size());double db =1.0 / (b.v.back() + (b.get_pos((unsigned)b.v.size() - 2) / 1e4) +(b.get_pos((unsigned)b.v.size() - 3) + 1) / 1e8);for (int i = (int)v.size() - 1; i >= 0; i--) {c.v.insert(c.v.begin(), v[i]);int m = (int)((c.get_pos((int)e.v.size()) * 10000 +c.get_pos((int)e.v.size() - 1)) *db);c = c - e * m, c.setsign(c.sign, 0), d.v[i] += m;while (!(c < e)) c = c - e, d.v[i] += 1;}return d.setsign(sign * b.sign, 0);}bigint operator%(const bigint &b) const { return *this - *this / b * b; }bool operator>(const bigint &b) const { return b < *this; }bool operator<=(const bigint &b) const { return !(b < *this); }bool operator>=(const bigint &b) const { return !(*this < b); }bool operator!=(const bigint &b) const { return !(*this == b); }friend std::ostream &operator<<(std::ostream &os, const bigint &b) {os << b.to_str();return os;}friend std::istream &operator>>(std::istream &is, bigint &b) {std::string s;is >> s;b = s;return is;}
};
int main()
{ios::sync_with_stdio(0);cin.tie(0);ll tttt=1;cin>>tttt;while(tttt--){bigint a,b,c,x,y,d;cin>>a>>b>>c>>x>>y>>d;if(d<=x){cout<<a<<'\n';continue;}else if(d<=x+y){bigint ans=a+(d-x)*b;if(d%x<=y)ans=min(ans,d/x*a+min(a,d%x*b));else ans=min(ans,(d/x+1)*a);cout<<ans<<'\n';}else{bigint ans=a+b*y+c*(d-x-y);ans=min(ans,(d/(x+y))*(a+b*y)+d%(x+y)*c);if(d%(x+y)>x)ans=min(ans,(d/(x+y))*(a+b*y)+a+b*(d%(x+y)-x));else ans=min(ans,(d/(x+y))*(a+b*y)+a-b*min(d/(x+y)*y,x-d%(x+y)));if(d%x<=y)ans=min(ans,d/x*a+min(a,d%x*b));else ans=min(ans,(d/x+1)*a);ans=min(ans,a*(d/x+1));if(d%x<=y)ans=min(ans,a*(d/x)+b*(d%x));else if(c<=b)ans=min(ans,a*(d/x)+b*y+c*(d%x-y));else ans=min(ans,a*(d/x)+b*min(d%x,d/x*y)+c*max((bigint)0,d%x-d/x*y));cout<<ans<<'\n';}}return 0;
}
F.The Hermit / 隐士
题意:在 1~m 的正整数集里面选大小为n的子集,问每个子集最多能保留几个数使得剩下的数的gcd不等于min。所有答案求和对998244353取模。
限制条件:\(m≤10^5\)。
考虑一下一个子集如何删掉元素。如果不删这个子集的最小值,显然剩下的数的gcd和min都是不变的,等于没删,因此每次都要删掉当前的最小值。那就变成了每个子集要删掉多少次最小值,我们可以反过来考虑每个数要被删掉几次。
如果数 \(x\) 要删掉,也就是说所有的数都是 \(x\) 的倍数,这时候后面的数可能有0到 \(n-1\) 个。如果后面的数不是 \(n-1\) 个,那么前面就有一些数比 \(x\) 先删掉,显然这些数都会是 \(x\) 的因子,并且它们之间后一个都是前一个的倍数。我们记 \(f_{c,x}\) 为满足上述条件形成了 \(c\) 个数,并且最后一个数是 \(x\) 的方案数,而 \(x\)后面的数显然就是从 \(x+1\) 到 \(m\) 里面选 \(n-c\) 个,也就是\(\binom{\lfloor m/x \rfloor-1}{n-c}\)。所以我们要求的就是\(\sum_{1≤c≤n,1≤x≤m} f_{c,x}\binom{\lfloor m/x \rfloor-1}{n-c}\)。
下面我们考虑一下怎么求 \(f_{c,x}\) 。首先 \(c\) 的最大值是不会超过 \(\lfloor\log_2 10^6\rfloor=19\) 的。那么考虑每个 \(x\) 会贡献给谁,显然就是1到 \(m\) 里面 \(x\) 的倍数,也就是 \(f_{c+1,kx}+=f_{c,x}\) for k in 2~\(\lfloor m/x \rfloor\)。\(m+m/2+m/3+···+m/m\),这是经典的调和级数,时间复杂度 \(O(m\log m)\) ,再算上遍历c的 \(\log m\)次,预处理 \(f\) 数组的时间复杂度就是 \(O(m\log^2 m)\)。
然后枚举c和x遍历过去,每次还要求一个组合数,但也是 \(m/x\) 级别的数,预处理1到m的逆元以后很快就能求出来,这部分的时间复杂度证明可以见官方题解,我不会。这部分的时间复杂度是 \(O(m\log m \log \log m)\)。因此总时间复杂度为 \(O(m\log^2 m)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int M=998244353;
int main()
{ios::sync_with_stdio(0);cin.tie(0);ll tttt=1;//cin>>tttt;while(tttt--){ll m,n;cin>>m>>n;vector<vector<ll>>f(20,vector<ll>(m+7,0));vector<ll>inv(m+1,1);for(int i=2;i<=m;i++){inv[i]=(M-M/i)*inv[M%i]%M;}auto C=[&](ll n,ll m){if(m>n)return 0ll;ll ans=1;for(int i=m+1;i<=n;i++){ans=ans*i%M;}for(int i=1;i<=n-m;i++){ans=ans*inv[i]%M;}return ans;};for(int i=1;i<=m;i++)f[1][i]=1;for(int p=1;p<19;p++){for(int i=1;i<=m;i++){if(!f[p][i])continue;for(int j=2*i;j<=m;j+=i){f[p+1][j]=(f[p+1][j]+f[p][i])%M;}}}ll ans=0;for(int p=1;p<=min(n,19ll);p++){for(int i=1;i<=m;i++){ans=(ans+f[p][i]*C(m/i-1,n-p)%M)%M;}}cout<<(C(m,n)*n%M-ans+M)%M<<'\n';}return 0;
}
谢谢你的阅读!
