Preface
事實證明比賽前真得訓練一下,不然正賽的時候瘋狂發病真頂不住
本來想週四晚上 VP 下杭州或者上海,但好像都沒上 QOJ,遂找了這賽季的 CCPC 重慶聊以慰藉
本來以為計數場要寄了的,結果最後徐神絕殺 4:59 絕殺 D 之後也是來到 9 題區,感覺也沒這麼毒瘤
A. 乘積,尤拉函式,求和
很典的一個題,和暑假牛客多校的一個題幾乎一樣
由於尤拉函式 \(\phi(n)=n\times \prod \frac{p_i-1}{p_i}\),因此一個集合的貢獻可以分成兩部分,一個是直接乘積起來的部分,一個是後面對每個質因子只求一次貢獻的部分
考慮令 \(f(i,mask)\) 表示處理了前 \(i\) 個數,此時出現過的質因數集合的狀壓值為 \(mask\) 時,前面部分的貢獻是多少,轉移就是個揹包
直接做複雜度肯定不能接受,根據經典套路 \(\sqrt {3000}\) 以內的質數只有 \(16\) 個,因此我們可以只狀壓較小的質因子
之後將所有數按照最大質因子分組,顯然同一組內的貢獻可以一起討論,總複雜度 \(O(n\times 2^{16})\)
#include<cstdio>
#include<iostream>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
const int N=3005,all=1<<16,mod=998244353;
int n,a[N],vis[N],pri[N],id[N],cnt,f[2][1<<16];
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
struct ifo
{
int val,S;
inline ifo(CI VAL=0,CI NS=0)
{
val=VAL; S=NS;
}
}; vector <ifo> vec[N];
inline void init(CI n)
{
for (RI i=2;i<=n;++i)
{
if (!vis[i]) id[i]=cnt,pri[cnt++]=i;
for (RI j=0;j<cnt&&i*pri[j]<=n;++j)
if (vis[i*pri[j]]=1,i%pri[j]==0) break;
}
}
int main()
{
init(3000); scanf("%d",&n);
for (RI i=1;i<=n;++i)
{
scanf("%d",&a[i]);
ifo cur(a[i],0);
for (RI j=0;j<16;++j)
if (a[i]%pri[j]==0)
{
cur.S|=(1<<j);
while (a[i]%pri[j]==0) a[i]/=pri[j];
}
if (a[i]>1) vec[id[a[i]]].push_back(cur);
else vec[0].push_back(cur);
}
f[0][0]=1;
for (auto [val,S]:vec[0])
{
for (RI mask=0;mask<all;++mask) f[1][mask]=f[0][mask];
for (RI mask=0;mask<all;++mask) inc(f[1][mask|S],1LL*f[0][mask]*val%mod);
for (RI mask=0;mask<all;++mask) f[0][mask]=f[1][mask];
}
for (RI mask=0;mask<all;++mask) f[1][mask]=0;
for (RI i=16;i<cnt;++i)
{
if (vec[i].empty()) continue;
for (auto [val,S]:vec[i])
{
static int g[2][1<<16];
for (RI mask=0;mask<all;++mask) g[0][mask]=f[0][mask],g[1][mask]=f[1][mask];
for (RI mask=0;mask<all;++mask)
{
inc(g[1][mask|S],1LL*f[1][mask]*val%mod);
inc(g[1][mask|S],1LL*f[0][mask]*(val*(pri[i]-1)/pri[i])%mod);
}
for (RI mask=0;mask<all;++mask) f[0][mask]=g[0][mask],f[1][mask]=g[1][mask];
}
for (RI mask=0;mask<all;++mask) inc(f[0][mask],f[1][mask]),f[1][mask]=0;
}
int ans=0;
for (RI mask=0;mask<all;++mask)
{
int cur=f[0][mask];
for (RI i=0;i<16;++i)
if ((mask>>i)&1) cur=1LL*cur*(pri[i]-1)%mod*quick_pow(pri[i])%mod;
inc(ans,cur);
}
return printf("%d",ans),0;
}
B. osu!mania
簽到,注意對浮點數的處理儘可能不要丟精度
#include<cstdio>
#include<iostream>
#include<cmath>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
int t,ppmax,a,b,c,d,e,f;
signed main()
{
for (scanf("%lld",&t);t;--t)
{
scanf("%lld%lld%lld%lld%lld%lld%lld",&ppmax,&a,&b,&c,&d,&e,&f);
int Acc=round(1.0L*(10000*(300*a+300*b+200*c+100*d+50*e))/(300*(a+b+c+d+e+f)));
int A=Acc/100,B=Acc%100; printf("%lld.",A);
if (B<10) printf("0%lld%% ",B); else printf("%lld%% ",B);
int up=320*a+300*b+200*c+100*d+50*e,dn=320*(a+b+c+d+e+f);
// up/dn<=4/5
if (5*up<=4*dn) printf("0\n"); else
{
//up/dn-4/5=(5*up-4*dn)/(5*dn)
int pp=round(1.0L*(5*ppmax*(5*up-4*dn))/(5*dn));
printf("%lld\n",pp);
}
}
return 0;
}
C. 連方
徐神開場寫的,我題目都沒看
#include <bits/stdc++.h>
void work() {
int n; std::cin >> n;
std::string a, b; std::cin >> a >> b;
std::string full(n, '#'), empty(n, '.'), ans[7];
if(a == full && b == full) {
std::cout << "Yes\n";
for(int i = 0; i < 7; ++i) std::cout << full << char(10);
return ;
}
if(a == empty || b == empty) {
std::cout << "Yes\n";
if(a == empty) for(int i = 0; i < 7; ++i) std::cout << b << char(10);
else for(int i = 0; i < 7; ++i) std::cout << a << char(10);
return ;
}
if(a == full || b == full) {
std::cout << "No\n";
return ;
}
ans[0] = a, ans[6] = b;
for(int i = 1; i < 6; ++i) ans[i] = empty;
int l, r;
for(int i = 0; i < n; ++i) {
if(a[i] == '#') ans[1][i] = '.'; else ans[1][i] = '#';
if(b[i] == '#') ans[5][i] = '.'; else ans[5][i] = '#';
}
for(int i = 0; i < n; ++i) {
if(ans[1][i] == '#') continue;
if(i > 0 && ans[1][i - 1] == '#' || i < n - 1 && ans[1][i + 1] == '#') {
ans[2][l = i] = '#';
break;
}
}
for(int i = 0; i < n; ++i) {
if(ans[5][i] == '#') continue;
if(i > 0 && ans[5][i - 1] == '#' || i < n - 1 && ans[5][i + 1] == '#') {
ans[4][r = i] = '#';
break;
}
}
if(l > r) std::swap(l, r);
if(std::abs(l - r) <= 1) ans[3][l] = '#'; else {
for(int i = l + 1; i <= r - 1; ++i) ans[3][i] = '#';
}
std::cout << "Yes\n";
for(int i = 0; i < 7; ++i) std::cout << ans[i] << char(10);
return ;
}
int main() {
std::ios::sync_with_stdio(false);
int T; std::cin >> T; while(T--) work();
return 0;
}
D. 有限小數
徐神和祁神一直在討論這個題,我當時在想+寫 H 就沒咋參與
本來一直 WA 最後神秘改了下列舉的上界就過了,十分神奇
#include <bits/stdc++.h>
using i128 = __int128_t;
using llsi = long long;
constexpr int c2 = 40, c5 = 20;
template <typename T>
T myabs(T a) {
return a < 0 ? -a : a;
}
i128 p2[101] = {1}, p5[101] = {1};
i128 w;
i128 exgcd(i128 a, i128 b, i128 &x, i128 &y) {
if (0==b) { x = 1; y = 0; return a; }
i128 ret = exgcd(b, a % b, y, x);
y -= a / b * x;
return ret;
}
i128 calc(i128 a, i128 b, i128 c) {
i128 x0, y0;
i128 g = exgcd(a, b, x0, y0);
// assert(a * x0 + b * y0 == g);
if (c % g != 0) return -1;
x0 *= c / g, y0 *= c / g;
i128 val = myabs(b / g);
x0 = (x0 % val + val) % val;
// if (0 == x0) x0 = val;
return x0;
}
void work() {
llsi _a, _b; std::cin >> _a >> _b;
i128 a = _a, b = _b;
llsi c, d = -1;
for(int x = 0; x <= c2; ++x) for(int y = 0; y <= c5; ++y) {
if(b * w % (p2[x] * p5[y]) != 0) break;
i128 nd = b * w / (p2[x] * p5[y]), nc = calc(- p2[x] * p5[y], b, a * w);
if(nc < 0) continue;
if(nd > 1'000'000'000) continue;
// std::cerr << std::format("x = {}, y = {}, c = {}, d = {}\n", p2[x], p5[y], nc, nd);
if(d < 0 || nc < c || nc == c && nd < d) c = nc, d = nd;
}
std::cout << c << " " << d << std::endl;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr), std::cout.tie(nullptr);
for(int i = 1; i <= std::max(c2, c5); ++i) p2[i] = p2[i - 1] * 2, p5[i] = p5[i - 1] * 5;
w = p2[30] * p5[13];
// std::cout << i128(w) << char(10);
int T; std::cin >> T; while(T--) work();
return 0;
}
E. 合成大西瓜
手玩一波會發現答案就是所有度數不為一的點權值的最大值;以及所有度數為一的點權值的次大值;再求一個 \(\max\) 即可
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
int n, m, A[N], deg[N], mx2, mx1, sd1;
signed main() {
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> m;
for (int i=1; i<=n; ++i) cin >> A[i];
for (int i=1; i<=m; ++i) {
int x, y; cin >> x >> y;
++deg[x], ++deg[y];
}
if (1==n) {
cout << A[1] << '\n';
return 0;
}
mx2 = mx1 = sd1 = -1;
for (int i=1; i<=n; ++i) {
if (1==deg[i]) {
if (A[i]>=mx1) sd1 = mx1, mx1 = A[i];
else if (A[i]>sd1) sd1 = A[i];
} else {
mx2 = max(mx2, A[i]);
}
}
cout << max(mx2, sd1) << '\n';
return 0;
}
H. str(list(s))
想清楚了其實很好寫,注意到只有 '
是特殊的,因此我們需要特別記錄 ‘
的個數
令 \(f_{i,j}\) 表示 \(s^i\) 下標模 \(p\) 後為 \(j\) 的所有非 '
字元的貢獻;\(g_{i,j}\) 表示 \(s^i\) 下標模 \(p\) 後為 \(j\) 的 '
數量;\(num_{i,j}\) 表示 \(s^i\) 下標模 \(p\) 後為 \(j\) 的所有字元的數量
轉移的時候按照題目模擬一下即可,注意特判開頭和結尾的變化,複雜度 \(O(|S|+kp)\)
#include<cstdio>
#include<iostream>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005,M=3005,mod=1e9+7;
char s[N]; int k,p,f[M][M],g[M][M],num[M][M];
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
int main()
{
scanf("%s%d%d",s,&k,&p);
int n=strlen(s);
for (RI i=0;i<n;++i)
{
++num[0][i%p];
if (s[i]=='\'') ++g[0][i%p]; else inc(f[0][i%p],s[i]);
}
n=(n-1+p)%p;
for (RI i=0;i<k;++i)
{
inc(f[i+1][0],(1LL*(num[i][0]-1+mod)%mod*' '%mod+'[')%mod);
inc(f[i+1][(5*n+4)%p],(1LL*(num[i][n]-1+mod)%mod*','%mod+']')%mod);
for (RI j=0;j<p;++j)
{
inc(f[i+1][(5*j+2)%p],f[i][j]);
// inc(f[i+1][(5*j+1)%p],1LL*(num[i][j]-g[i][j]+mod)*'\''%mod);
// inc(f[i+1][(5*j+3)%p],1LL*(num[i][j]-g[i][j]+mod)*'\''%mod);
inc(g[i+1][(5*j+1)%p],(num[i][j]-g[i][j]+mod)%mod);
inc(g[i+1][(5*j+3)%p],(num[i][j]-g[i][j]+mod)%mod);
if (j!=0) inc(f[i+1][(5*j+0)%p],1LL*num[i][j]*' '%mod);
if (j!=n) inc(f[i+1][(5*j+4)%p],1LL*num[i][j]*','%mod);
}
for (RI j=0;j<p;++j)
{
inc(g[i+1][(5*j+2)%p],g[i][j]);
inc(f[i+1][(5*j+1)%p],1LL*g[i][j]*'"'%mod);
inc(f[i+1][(5*j+3)%p],1LL*g[i][j]*'"'%mod);
}
for (RI j=0;j<p;++j) for (RI k=0;k<5;++k)
inc(num[i+1][(5*j+k)%p],num[i][j]);
n=(5*n+4)%p;
}
for (RI i=0;i<p;++i)
printf("%d ", int((f[k][i]+1LL*g[k][i]*'\''%mod)%mod));
// printf("%c", char((f[k][i]+1LL*g[k][i]*'\''%mod)%mod));
return 0;
}
I. 算術
注意到除了 \(1\) 之外的所有卡片一定是直接乘起來最優,現在的問題就是 \(1\) 要怎麼使用
一種是拿兩個 \(1\) 湊出一個新的 \(2\);另一種是不停地把剩下的 \(1\) 加到做乘法的數中最小的上面
由於 \(a_i\le 100\),我們可以直接列舉新生成了多少個 \(2\),剩下的部分模擬一下找個最優的方案即可
現在的問題是怎麼比較兩種方案的大小,由於式子是連乘的形式因此直接取對數後相加即可
#include<cstdio>
#include<iostream>
#include<cmath>
#define RI register int
#define CI const int&
using namespace std;
const int N=15,mod=998244353;
int t,a[N],b[N]; long double lg[105];
int main()
{
for (RI i=1;i<=100;++i) lg[i]=logl(i);
for (scanf("%d",&t);t;--t)
{
for (RI i=1;i<=9;++i) scanf("%d",&a[i]);
long double mx=-1; int cs;
for (RI i=0;2*i<=a[1];++i)
{
for (RI j=1;j<=9;++j) b[j]=a[j];
b[1]-=2*i; b[2]+=i;
for (RI j=2;j<9&&b[1];++j)
{
int tmp=min(b[1],b[j]);
b[1]-=tmp; b[j]-=tmp; b[j+1]+=tmp;
}
long double cur=0;
if (b[1]>0)
{
if (b[9]==0) cur=0; else
{
int add=b[1]/b[9],rem=b[1]%b[9];
cur+=lg[9+add+1]*rem;
cur+=lg[9+add]*(b[9]-rem);
}
} else
{
for (RI j=2;j<=9;++j) cur+=lg[j]*b[j];
}
if (cur>mx) mx=cur,cs=i;
}
for (RI i=1;i<=9;++i) b[i]=a[i];
b[1]-=2*cs; b[2]+=cs;
for (RI i=2;i<9&&b[1];++i)
{
int tmp=min(b[1],b[i]);
b[1]-=tmp; b[i]-=tmp; b[i+1]+=tmp;
}
int ans=1;
if (b[1]>0)
{
if (b[9]==0) ans=1; else
{
int add=b[1]/b[9],rem=b[1]%b[9];
for (RI i=1;i<=rem;++i) ans=1LL*ans*(9+add+1)%mod;
for (RI i=1;i<=b[9]-rem;++i) ans=1LL*ans*(9+add)%mod;
}
} else
{
for (RI i=2;i<=9;++i)
for (RI j=1;j<=b[i];++j) ans=1LL*ans*i%mod;
}
printf("%d\n",ans);
}
return 0;
}
J. 骰子
純簽到,直接 Gauss 一定每個面都能湊到 \(6\) 即可
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int n,m;
int main()
{
scanf("%d%d",&n,&m);
return printf("%d",6*n*m),0;
}
K. 小 C 的神秘圖形
看了眼就扔給祁神了,然後被祁神秒了
#include<bits/stdc++.h>
using namespace std;
signed main() {
ios::sync_with_stdio(0); cin.tie(0);
int n; cin >> n;
string str1, str2;
cin >> str1 >> str2;
bool ok=true;
for (int i=0; i<n; ++i) {
if (str1[i]!='1' && str2[i]!='1') {ok=false; break;}
}
cout << (ok ? "1\n" : "0\n");
return 0;
}
Postscript
這場感覺題太偏向於計數了,導致後期基本沒啥事情幹,怪不得 downvote 這麼多
據說 BIT 也很喜歡出計數,希望昆明能來點適合牢閃的題的說