2020-2021 ACM-ICPC, Asia Seoul Regional Contest
Solved:
- B、Commemorative Dice
- C、Dessert Café
- E、Imprecise Computer
- G、Mobile Robot
- H、Needle
B、Commemorative Dice
签到
int gcd(int a,int b)
{
if(b == 0)return a;
return gcd(b,a%b);
}
int main()
{
int a[10],b[10];
for(int i=1;i<=6;i++)scanf("%d",&a[i]);
for(int i=1;i<=6;i++)scanf("%d",&b[i]);
int sum=0;
for(int i=1;i<=6;i++){
for(int j=1;j<=6;j++){
if(a[i]>b[j])sum++;
}
}
int num=gcd(sum,36);
printf("%d/%d",sum/num,36/num);
}
C、Dessert Café
题意:
题目给出一棵树,树上有一些结点是特殊点,问在这些特殊点之间的路径上(包括特殊点在内)一共有多少个结点。
想法:
考虑对整棵树进行 \(DFS\) ,从一个点开始,去求这个结点的子树中拥有特殊点的个数,如果某个点的子树中特殊点的个数在 \(2\sim (k-1)\) 之间,那么这个点就在特殊点的之间的路径上。
代码:
int n,k;
vector<int>mp[maxn];
int num[maxn];
bool isA[maxn];
int ans=0;
void dfs(int u,int f)
{
int cnt=0;
for(int i=0;i<mp[u].size();i++)
{
int v=mp[u][i];
if(v!=f)
{
dfs(v,u);
num[u]+=num[v];
if(num[v]) cnt++;
}
}
if(num[u]!=k) cnt++;
//dbg(u);
if(cnt>=2&&!isA[u]) ans++;
//dbg(ans);
}
void run()
{
n=rd();
k=rd();
for(int i =1;i<n;i++)
{
int u=rd(),v=rd(),w=rd();
mp[u].push_back(v);
mp[v].push_back(u);
}
for(int i=0;i<k;i++)
{
int x=rd();
num[x]=1;
isA[x]=true;
ans++;
//cout<<ans<<" sss"<<endl;
}
dfs(1,-1);
cout<<ans<<endl;
}
signed main()
{
run();
return 0;
}
E、Imprecise Computer
队友tql
int n,k;
int dp[maxn];
void run()
{
n=rd();
for(int i=1;i<=n;i++) dp[i]=rd();
bool flag=1;
for(int i=1;i<=n;i++)
{
if(dp[i]>=2) {
flag=0;
break ;
}
else{
if(dp[i]) {
if(dp[i+1]) dp[i+1]--;
else dp[i+1]++;
}
}
}
puts((flag&&dp[n]==0)?"YES":"NO");
}
signed main()
{
run();
return 0;
}
G、Mobile Robot
题意:
\(n\) 个机器人,编号从 \(1 \sim n\),告诉你每个机器人在坐标轴上的位置,问通过对每个机器人的移动,让编号 \(i \ 和\ i-1\)号机器人($i\in \left[ 2,n\right] $)的距离保持在 \(d\),问所有机器人中移动距离的最大值的最小值是多少。
想法:
首先为了达到题目要求,就必须让每个机器人在坐标轴上的排列从 \(1\sim n\),或者是 \(n\sim 1\),只有这样才能是满足题目条件的。在此条件的基础上,我们去考虑移动的距离问题。因为在坐标轴上,我们的操作就是把 \(1\sim n\)编号的机器人移动每个指定点即可,而这个指定点是只要确定一个,就可以确定全部。我们可以设指定点为 \(x+d、x+2\times d、x+3\times d......\),那么我们就可以求出每个点移动的距离,然后可以求出带未知数 \(x\) 的最大移动值 \(maxn\) 和最小移动值 \(minn\)。然后我们考虑怎么让最大值的最小,并且最大值还是最大,很显然,我们可以通过 \(maxn-minn\) 求出最大值的最小值,而在减的过程中,x也减掉了。从而得到答案。需要注意,还需要考虑 \(n\sim 1\)排列,即把机器人的编号反过来即可。
代码:
ll a[maxn],b[maxn];
int main()
{
ll n,m;
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
b[n-i+1]=a[i];
}
ll maxx=-INF,minx=INF;
for(int i=1;i<=n;i++){
maxx=max(maxx,a[i]-1ll*i*m);
minx=min(minx,a[i]-1ll*i*m);
}
ll ans=maxx-minx;
maxx=-INF,minx=INF;
for(int i=1;i<=n;i++){
maxx=max(maxx,b[i]-1ll*i*m);
minx=min(minx,b[i]-1ll*i*m);
}
ans=min(ans,maxx-minx);
if(ans%2==1){
printf("%lld.5",ans/2);
}else{
printf("%lld.0",ans/2);
}
}
H、Needle
题意:
给你三个数组 \(a、b、c\),问在这些数组中存在多少组 \((i,j,k)\) 满足 \(a[i]+c[k]=2\times b[j]\)。
想法:
-
\(n\) 很大,暴力肯定会超时。考虑多项式乘法,把数组 \(a,b\) 各自作为两个多项式中 \(x\) 的系数,最后把两个多项式乘起来,然后只需要看 \(2\times b[j]\)作为次数在这个多项式中系数的值即可,系数为几,就有几种方案。
-
对于多项式乘法,用 \(NTT\) 完成即可,也就是一到快速数论变换的裸题。
-
需要注意的就是数组中的数的范围是 \(-30000\sim 30000\),我们要的多项式次数需要正数,因此给每个数加上 \(30000\) 即可。
代码:
const int N = 300010;
const int Mod = 998244353;
int n,m,L,R[N],g[N],a[N],b[N];
LL x[N],ci[N];
int gi()
{
int x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
}
inline int QPow(int d,int z)
{
int ans=1;
for(;z;z>>=1,d=1ll*d*d%Mod)
if(z&1)ans=1ll*ans*d%Mod;
return ans;
}
inline void NTT(int *A,int f)
{
for(int i=0;i<n;++i)if(i<R[i])swap(A[i],A[R[i]]);
for(int i=1;i<n;i<<=1){
int gn=QPow(3,(Mod-1)/(i<<1)),x,y;
for(int j=0;j<n;j+=(i<<1)){
int g=1;
for(int k=0;k<i;++k,g=1ll*g*gn%Mod){
x=A[j+k];y=1ll*g*A[i+j+k]%Mod;
A[j+k]=(x+y)%Mod;A[i+j+k]=(x-y+Mod)%Mod;
}
}
}
if(f==1)return;reverse(A+1,A+n);
int y=QPow(n,Mod-2);
for(int i=0;i<n;++i)A[i]=1ll*A[i]*y%Mod;
}
int main(){
int m1,m2,m3,l=0;
long long c1,c2;
scanf("%d",&m1);
for(int i=0;i<m1;++i){
int num;
cin>>num;
a[num+30000]=1;
c1=max(c1,1ll*num+30000);
}
scanf("%d",&m2);
for(int i=0;i<m2;++i){
scanf("%lld",&x[i]);
x[i]+=30000;
}
scanf("%d",&m3);
for(int i=0;i<m3;++i){
int num;
cin>>num;
b[num+30000]=1;
c2=max(c2,1ll*num+30000);
}
c1--;
c2--;
for(c2+=c1,n=1;n<=c2;n<<=1,++L);
for(int i=0;i<n;++i)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
NTT(a,1);NTT(b,1);
for(int i=0;i<n;++i)a[i]=1ll*a[i]*b[i]%Mod;
NTT(a,-1);
//memset(ci,0,sizeof ci);
long long ans=0;
for(int i=0;i<m2;i++){
ans+=1ll*a[x[i]*2];
}
cout<<ans;
return 0;
}