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;
}
posted @ 2023-01-13 16:53  Forever1507  阅读(31)  评论(0编辑  收藏  举报