CF1926(div4)题解
C - Vlad and a Sum of Sum of Digits
题意:
$ 一个数n的贡献为n的数位和 $
$ 给定一个数n,求1 ~\ n的所有数的贡献,每个样例限制0.5s $
思路:
- 预处理每个答案,$ O(1) $ 查询
- 一个数的贡献为 $ f_i=f_{i/10}+i \%\ 10 ,求出前缀和即可,时间复杂度O(n) $
- 暴力计算,时间复杂度 $ O(nlogn) $
1 2 3 4 5 6 7 8 9 10 11 12
int f[N],g[N]; void init(){ for(int i=1;i<=200000;i++){ f[i]=f[i/10]+i%10; g[i]=g[i-1]+f[i]; } } void solve(){ int n; cin>>n; cout<<g[n]<<"\n"; }
第二种方法:赛时想多了的做法,每个样例复杂度 $ O(logn) $
枚举每一位数字,每次将n分成 $ l+x+r $ 的形式,l
为该数位左边的数,r
为右边,x
为数位本身
0-9
均出现了l
次,1~(x-1)
出现多一次,这些出现的次数pow即为这些数字的总贡献,即 $ [l45+x(x-1)/2]pow $- 当前数位
x
的贡献还要加上r+1
次1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
void solve(){ int n; cin>>n; ll res=0; ll ans=0; ll num=1; int nn=n; while(nn){ int x=nn%10; ll l=n/(num*10); ll r=num==1?0:n%num; res+=(l*45+x*(x-1)/2)*num+x*(r+1); nn/=10; num*=10; } cout<<res<<"\n"; }
F - Vlad and Avoiding X
题意:
给定一个7x7的方针,有黑白两种方针,不允许出现黑色叉(自身,左上,左下,右上,右下为黑色)的最小改变次数
思路:
- 暴力dfs,从左到右,从上到下枚举左上端点为黑色的点,如果存在叉,则任意改变一个黑色,继续向前移动,时间复杂度 $ O(5^{5 \times 5}) $(最多枚举5*5面积,每个点修改5次),过大,考虑优化
- 发现对角线上不同的奇偶互不影响,因此可以分开操作,时间复杂度变为 $ O(5^{13}+5^{12}) $大大降低了复杂度
- 可以发现修改当前点只能改变当前的叉,修改其他点能改变至少两个叉,因此可以不用修改当前点,复杂度降为 $ O(4^{13}+4^{12}) $
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
char a[10][10]; void solve(){ for(int i=1;i<=7;i++){ for(int j=1;j<=7;j++)cin>>a[i][j]; } int ans=0; for(int p=0;p<2;p++){ int res=100; function<void(int,int,int)> dfs=[&](int x,int y,int z){ if(x==6){ res=min(res,z); return; } if(y==6){ dfs(x+1,1,z); return; } if((x+y)%2!=p){ dfs(x,y+1,z); return; } if(a[x][y]=='B'&&a[x+1][y+1]=='B'&&a[x+2][y]=='B'&&a[x+2][y+2]=='B'&&a[x][y+2]=='B'){ a[x+1][y+1]='W'; dfs(x,y+1,z+1); a[x+1][y+1]='B'; a[x+2][y+2]='W'; dfs(x,y+1,z+1); a[x+2][y+2]='B'; a[x+2][y]='W'; dfs(x,y+1,z+1); a[x+2][y]='B'; a[x][y+2]='W'; dfs(x,y+1,z+1); a[x][y+2]='B'; return; } dfs(x,y+1,z); }; dfs(1,1,0); ans+=res; } cout<<ans<<"\n"; }
G - Vlad and Trouble at MIT(树形dp)
题意:
有三种人,P
在演奏音乐,C
不在乎,S
在睡觉,一棵树,问最少需要多少块隔板将P
和S
分离 思路:
忽略C
,将两种人看作红蓝两种液体,即不让二者混合
从下往上dp, $ dp_{i,0}表示在i处只允许S通过,dp_{i,1}表示只允许P通过 $ - 初始化
- $ s_i=S,dp_{i,0}=0,dp_{i,1}=inf $
- $ s_i=P,dp_{i,1}=0,dp_{i,0}=inf $
- 转移
- $ dp_{i,0}+=min(dp_{v,0},dp_{v,1}+1) $
- $ dp_{i,1}+=min(dp_{v,1},dp_{v,0}+1) $
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
void solve(){ int n; cin>>n; vector<int> adj[n+1]; for(int i=2;i<=n;i++){ int x; cin>>x; adj[x].push_back(i); adj[i].push_back(x); } string s; cin>>s; s=" "+s; vector<vector<ll>> dp(n+1,vector<ll>(2));//0表示允许睡觉通过,1表示允许音乐通过 function<void(int,int)> dfs=[&](int u,int fa){ if(s[u]!='P')dp[u][0]=0; else dp[u][0]=INF; if(s[u]!='S')dp[u][1]=0; else dp[u][1]=INF; for(auto v:adj[u]){ if(v==fa)continue; dfs(v,u); dp[u][0]=dp[u][0]+min(dp[v][0],dp[v][1]+1); dp[u][1]=dp[u][1]+min(dp[v][1],dp[v][0]+1); } }; dfs(1,0); cout<<min(dp[1][0],dp[1][1])<<"\n"; }
This post is licensed under CC BY 4.0 by the author.