2020 ICPC Asia TW Site
本来只要写 \(5\) 道题的结果写到一半被加了一道(
C-Pyramid
因为初始状态是 \(L\),所以最终的状态与经过它的球的数量的奇偶性相关!技术就是 \(R\),反之 \(L\)。
然后只要求前 \(k-1\) 个球里面,经过了每个节点多少次就可以了!
但是怎么算呢?
显然第奇数次经过的会往左边走,剩下的走右边。那么假设现在这个点有 \(x\) 了球经过,向左边走的就有 \(\lceil{\frac{x}{2}}\rceil\) 个,往右边走的就有 \(\lfloor{\frac{x}{2}}\rfloor\) 个!
然后从顶点往下递推就可以了。
把状态求出来,模拟第 \(k\) 个球的运动即可。
查看代码
#include<bits/stdc++.h>
using namespace std;
int t,n,k;
int a[50005005];
int calc(int i,int j){
return i*(i-1)/2+j;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
memset(a,0,sizeof(a));
cin>>n>>k;
a[1]=k-1;
for(int i=2;i<=n;++i){
for(int j=1;j<=i;++j){
if(j!=1)a[calc(i,j)]+=a[calc(i-1,j-1)]/2;
if(j!=i)a[calc(i,j)]+=(a[calc(i-1,j)]+1)/2;
}
}
int i=1,j=1;
while(1){
if(i==n){
cout<<j-1<<'\n';
break;
}
if(a[calc(i,j)]%2==1)++j;
++i;
}
}
return 0;
}
这个是 \(\Theta(n^2)\) 的。
E-A Color Game
看到这个题想到了小学时某年寒假一些不好的回忆
首先可以看出这是个区间 dp。
状态比较特殊:\(dp_{l,r,k}\) 表示把区间 \([l,r]\) 所有元素删得只剩 \(k\) 时最多能剩几个。然后开个辅助状态表示 \([l,r]\) 能不能删完。
枚举区间长、起点、断点的元素类型后,方程如下:
查看代码
if(dp[i][k][c]&&dp[k+1][j][c]){
dp[i][j][c]=max(dp[i][j][c],dp[i][k][c]+dp[k+1][j][c]);
}
if(dp[i][k][c]&&dp[k+1][j][c]&&dp[i][k][c]+dp[k+1][j][c]>=m){
flg[i][j]=1;
}
if(flg[i][k]){
dp[i][j][c]=max(dp[i][j][c],dp[k+1][j][c]);
}
if(flg[k+1][j]){
dp[i][j][c]=max(dp[i][j][c],dp[i][k][c]);
}
就没事了!
F-Cable Protection
基环树上 dp!
首先考虑如果是树怎么办。
考虑树形 dp,设 \(dp_{i,0/1}\) 表示把以 \(i\) 为根节点的子树全部覆盖的最小答案,当前这个点选或者不选。
然后只要随机在环里面断开一条边,以两边分别为根节点跑 dp 并取最小值即可。
点击查看代码
#include<bits/stdc++.h>
//#define int long long
using namespace std;
int n,m,dp[2000005][2],x,y;
vector<int>nbr[2000005];
void dfs(int cur,int fa){
dp[cur][1]=dp[cur][0]=0;
// if(nbr[cur].size()==1&&nbr[cur][0]==fa)dp[cur][0]=1e9;
for(auto to:nbr[cur]){
if(to==fa)continue;
dfs(to,cur);
dp[cur][0]+=dp[to][1];
dp[cur][1]+=min(dp[to][0],dp[to][1]);
}
dp[cur][1]++;
return;
}
signed main(){
// freopen("F2.in","r",stdin);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
bool flg=0;
for(int i=0;i<n+m;++i){
int u,v;
cin>>u>>v;
if(!flg&&u<n&&v<n){
flg=1;
x=u,y=v;
continue;
}
nbr[u].push_back(v);
nbr[v].push_back(u);
}
dfs(x,-1);
int ans=dp[x][1];
memset(dp,0,sizeof(dp));
dfs(y,-1);
ans=min(ans,dp[y][1]);
cout<<ans;
return 0;
}
H-Optimization for UltraNet
要求最小边最大,最大生成树!
要求边权和最小,最小生成树!
怎么结合上面这俩呢?
因为第一个条件更加优先,先跑最大生成树!
找到最小的一条边,把边权大于这个的边再跑一次最小生成树就可以了!
然后按边权从大到小用并查集合并,贡献就是并查集两端的 \(siz\) 的乘积(乘法原理)乘边权!
查看代码
#include<bits/stdc++.h>
//#define int long long
using namespace std;
int n,m,dp[2000005][2],x,y;
vector<int>nbr[2000005];
void dfs(int cur,int fa){
dp[cur][1]=dp[cur][0]=0;
// if(nbr[cur].size()==1&&nbr[cur][0]==fa)dp[cur][0]=1e9;
for(auto to:nbr[cur]){
if(to==fa)continue;
dfs(to,cur);
dp[cur][0]+=dp[to][1];
dp[cur][1]+=min(dp[to][0],dp[to][1]);
}
dp[cur][1]++;
return;
}
signed main(){
// freopen("F2.in","r",stdin);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
bool flg=0;
for(int i=0;i<n+m;++i){
int u,v;
cin>>u>>v;
if(!flg&&u<n&&v<n){
flg=1;
x=u,y=v;
continue;
}
nbr[u].push_back(v);
nbr[v].push_back(u);
}
dfs(x,-1);
int ans=dp[x][1];
// for(int i=0;i<n+m;++i)cout<<"Test: "<<dp[i][0]<<' '<<dp[i][1]<<'\n';
memset(dp,0,sizeof(dp));
dfs(y,-1);
ans=min(ans,dp[y][1]);
cout<<ans;
return 0;
}
K-Number with Bachelors
神秘数位 dp。
学习到的东西:输入 \(16\) 进制用 hex
,换回 \(10\) 进制用 dec
!
然后直接用搜索写数位 dp 即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ull=unsigned long long;
#define int long long
//#pragma GGC optimize(2)
int n,a[18];
ull dp[2][18][2<<17];
//位数 状压每个数是否出现 到没到上线 是否有前导0 10 or 16进制
ull dfs(int tot,int st,bool lim,bool flg,bool base){
if(!tot)return 1;
if(flg&&lim&&dp[base][tot][st]!=1e18+1)return dp[base][tot][st];
int x=(lim?(base?15:9):a[tot]);
ull ans=0;
for(int i=0;i<=x;++i){
if(!i){
if(flg){
if(st>>i&1)continue;
ans+=dfs(tot-1,st^(1<<i),lim||(i<x),flg||(i!=0),base);
}
else{
ans+=dfs(tot-1,st,lim||(i<x),flg||(i!=0),base);
}
}
else{
if(st>>i&1)continue;
ans+=dfs(tot-1,st^(1<<i),lim||(i<x),flg||(i!=0),base);
}
}
// cout<<tot<<" "<<st<<" ans = "<<ans<<endl;
if(flg&&lim)dp[base][tot][st]=ans;
return ans;
}
ull calc_10(ull x){
int tot=0;
while(x){
a[++tot]=x%10;
x/=10;
}
return dfs(tot,0,0,0,0);
}
ull calc_16(ull x){
int tot=0;
while(x){
a[++tot]=x%16;
x/=16;
}
return dfs(tot,0,0,0,1);
}
void work_10(){
int opt;
cin>>dec>>opt;
if(opt==0){
ull a,b;
cin>>dec>>a>>dec>>b;
if(a>1e9)a=1e9;
if(b>1e9)b=1e9;
// cout<<"Hello"<<endl;
if(a==0)cout<<dec<<calc_10(b)<<'\n';
else cout<<dec<<calc_10(b)-calc_10(a-1)<<'\n';
}
else{
ull x;
cin>>dec>>x;
int lt=-1,rt=1e9+1;
while(lt+1<rt){
int mid=lt+rt>>1;
if(calc_10(mid)<x)lt=mid;
else{
rt=mid;
if(mid==0)break;
}
}
if(rt==1e9+1)cout<<"-\n";
else cout<<dec<<rt<<'\n';
}
}
void work_16(){
int opt;
cin>>dec>>opt;
if(opt==0){
ull a,b;
cin>>hex>>a>>hex>>b;
if(a==0)cout<<hex<<calc_16(b)<<'\n';
else cout<<hex<<calc_16(b)-calc_16(a-1)<<'\n';
}
else{
ull x;
cin>>hex>>x;
int lt=-1,rt=1e9+1;
while(lt+1<rt){
int mid=lt+rt>>1;
if(calc_16(mid)<x)lt=mid;
else{
rt=mid;
if(!mid)break;
}
}
if(rt==1e9+1)cout<<"-\n";
else cout<<hex<<rt<<'\n';
}
}
signed main(){
for(int i=0;i<=1;++i)for(int j=0;j<=17;++j)for(int k=0;k<(2<<17);++k)dp[i][j][k]=1e18+1;
cin>>n;
for(int i=1;i<=n;++i){
char c;
cin>>c;
if(c=='d')work_10();
else work_16();
}
return 0;
}