2022-2023 ICPC, Asia Yokohama Regional Contest 2022(题解)

2022-2023 ICPC, Asia Yokohama Regional Contest 2022

文章目录

  • A. Hasty Santa Claus
  • B. Interactive Number Guessing
  • C. Secure the Top Secret
  • D. Move One Coin
  • E. Incredibly Cute Penguin Chicks
  • F. Make a Loop
  • G. Remodeling the Dungeon
  • H. Cake Decoration
  • I. Quiz Contest
  • J. Traveling Salesperson in an Island
  • K. New Year Festival

A. Hasty Santa Claus

思路:贪心。将区间优先按照 a i a_i ai其次 b i b_i bi大小进行排序,然后从第 1 1 1天枚举至第 31 31 31天,每次取出前 k k k个house进行拜访。复杂度 O ( n log ⁡ 2 n ) O(n\log_2n) O(nlog2n)

#include<bits/stdc++.h>
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<int,int>
#define bit bitset<100000>
using namespace std;
const int MAX=3e5+10;
const int MOD=998244353; //G=3
const int INF=INT_MAX;
const double PI=acos(-1.0);
typedef int64_t ll;
struct lenka{int l,r,idx;};
bool cmp(lenka a,lenka b){return a.l==b.l?a.r<b.r:a.l<b.l;}
int ans[1100];
int solve()
{int n,k;cin>>n>>k;vector<lenka>a;a.resize(n);for(int i=0;i<n;i++)scanf("%d%d",&a[i].l,&a[i].r),a[i].idx=i;sort(a.begin(),a.end(),cmp);for(int i=1;i<=31;i++){vector<int>v;int m=k;for(int j=0;j<sz(a)&&m;j++){if(i<a[j].l)continue;v.push_back(j);ans[a[j].idx]=i;m--;}reverse(v.begin(),v.end());for(int j:v)a.erase(a.begin()+j);for(auto &j:a)j.l=max(j.l,i+1);sort(a.begin(),a.end(),cmp);}for(int i=0;i<n;i++)printf("%d\n",ans[i]);return 1;
}
int main()
{int T=1;//cin>>T;while(T--)solve();return 0;
}

B. Interactive Number Guessing

思路:考虑一位一位确定,二分区间 [ 0 , 9 ] [0,9] [0,9],当 x i + m i d ≥ 10 x_i+mid\ge10 xi+mid10时,会产生进位,数位和就会变少,否则会变多。

#include<bits/stdc++.h>
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<int,int>
#define bit bitset<100000>
using namespace std;
const int MAX=3e5+10;
const int MOD=998244353; //G=3
const int INF=INT_MAX;
const double PI=acos(-1.0);
typedef int64_t ll;
ll ask(ll x)
{cout<<"query "<<x<<endl;cin>>x;return x;
}
int solve()
{ll ans=0,pre=1;ll s=ask(0);for(ll i=0;i<18;i++){ll l=0,r=9;while(r>=l){ll ret=ask(pre*mid)-s;if(ret<0)r=mid-1;if(ret>0)l=mid+1;if(ret==0){if(mid==9)ans+=pre;break;}}ans+=pre*(9-mid);pre*=10;}cout<<"answer "<<ans<<endl;return 1;
}
int main()
{int T=1;//cin>>T;while(T--)solve();return 0;
}

C. Secure the Top Secret

题解

D. Move One Coin

思路:考虑将硬币的坐标排序后,用坐标的差分数组是否相同来判断是否匹配,因为至多只需移动一枚硬币即可匹配,所以2个图的差分数组不同项最多为2个,且一张图一个,当碰到不同项时,直接枚举删除硬币后判断是否匹配即可。复杂度 O ( H ∗ W ) O(H*W) O(HW)

#include<bits/stdc++.h>
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<int,int>
#define bit bitset<100000>
using namespace std;
const int MAX=1e6+10;
const int MOD=998244353; //G=3
const int INF=INT_MAX;
const double PI=acos(-1.0);
typedef int64_t ll;
struct lenka
{int n,m;char s[510][510];void read(){scanf("%d%d",&n,&m);for(int i=0;i<n;i++)scanf("%s",s[i]);}void rota(){char tmp[510][510];for(int i=0;i<m;i++)for(int j=0;j<n;j++)tmp[i][j]=s[j][i];swap(n,m);for(int i=0;i<n;i++)for(int j=0;j<m;j++)s[i][j]=tmp[i][m-1-j];}vector<pii> seri(){vector<pii>v;for(int i=0;i<n;i++)for(int j=0;j<m;j++){if(s[i][j]!='o')continue;v.push_back({i,j});}return v;}
};
pii operator-(pii a,pii b){return {a.fi-b.fi,a.se-b.se};}
pii operator+(pii a,pii b){return {a.fi+b.fi,a.se+b.se};}
int print(vector<pii> a){for(pii &kv:a)printf("%d %d\n",kv.se,kv.fi);return 1;}
int check(vector<pii> &a,vector<pii> &b)
{set<pii>s;for(int i=1;i<sz(a);i++)s.insert(a[i]-a[0]);for(int i=1;i<sz(b);i++){if(s.count(b[i]-b[0]))s.erase(b[i]-b[0]);else s.insert(b[i]-b[0]);}return s.size();
}
pii get(vector<pii> &a,vector<pii> &b)
{set<pii>s;for(int i=1;i<sz(a);i++)s.insert(a[i]-a[0]);for(int i=1;i<sz(b);i++)s.erase(b[i]-b[0]);return *s.begin();
}
int f(vector<pii> a,vector<pii> b)
{if(check(a,b)==0)return print({a[0],a[0]});if(sz(a)==2)return print({a[1],b[1]-b[0]+a[0]});if(check(a,b)>2){auto ta=a,tb=b;ta.erase(ta.begin());if(check(ta,b)==1)return print({a[0],ta[0]+get(b,ta)});tb.erase(tb.begin());if(check(tb,a)==1)return print({a[0]+get(a,tb),a[0]-(b[1]-b[0])});if(check(ta,tb)==0)return print({a[0],a[1]-(b[1]-b[0])});return 0;}assert(check(a,b)==2);print({a[0]+get(a,b),a[0]+get(b,a)});return 1;
}
int solve()
{lenka a,b;a.read();b.read();for(int i=0;i<6;i++){if(f(a.seri(),b.seri()))return 0;b.rota();}return 0;
}
int main()
{int T=1;//cin>>T;while(T--)solve();return 0;
}

E. Incredibly Cute Penguin Chicks

思路:用 C [ i ] , I [ i ] , P [ i ] C[i],I[i],P[i] C[i],I[i],P[i]分别代表各字母数量的前缀和,用 d [ i ] d[i] d[i]表示 [ 0 , i ] [0,i] [0,i]的方案数,则 d i = ∑ d j d_i=\sum d_j di=dj,当且仅当以下任一条件满足

  1. C i − C j = I i − I j , P i − P j > C i − C j C_i-C_j=I_i-I_j,P_i-P_j>C_i-C_j CiCj=IiIj,PiPj>CiCj
  2. C i − C j = P i − P j , I i − I j > C i − C j C_i-C_j=P_i-P_j,I_i-I_j>C_i-C_j CiCj=PiPj,IiIj>CiCj
  3. I i − I j = P i − P j , C i − C j > I i − I j I_i-I_j=P_i-P_j,C_i-C_j>I_i-I_j IiIj=PiPj,CiCj>IiIj

转移复杂度为 O ( n 2 ) O(n^2) O(n2),考虑优化,将上面条件式子转化一下得

  1. C i − I i = C j − I j , P i − C i > P j − C j C_i-I_i=C_j-I_j,P_i-C_i>P_j-C_j CiIi=CjIj,PiCi>PjCj
  2. C i − P i = C j − P j , I i − C i > I j − C j C_i-P_i=C_j-P_j,I_i-C_i>I_j-C_j CiPi=CjPj,IiCi>IjCj
  3. I i − P i = I j − P j , C i − I i > C j − I j I_i-P_i=I_j-P_j,C_i-I_i>C_j-I_j IiPi=IjPj,CiIi>CjIj

如此我们只需分别记住在 { C i − I i , P i − C i } , { C i − P i , I i − C i } , { I i − P i , C i − I i } \{C_i-I_i,P_i-C_i\},\{C_i-P_i,I_i-C_i\},\{I_i-P_i,C_i-I_i\} {CiIi,PiCi},{CiPi,IiCi},{IiPi,CiIi}下的 d i d_i di的值,再根据转化后的式子去dp转移。
因为式子中需要判断大小,我们可以预处理出所有可能的值并离散化,dp转移时二分查找求区间和,并记录下当前dp值,用树状数组来求区间和以及单点修改。复杂度 O ( n log ⁡ 2 n ) O(n\log_2n) O(nlog2n)

#include<bits/stdc++.h>
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<ll,ll>
#define bit bitset<100000>
using namespace std;
const int MAX=1e6+10;
const int MOD=998244353; //G=3
const int INF=INT_MAX;
const double PI=acos(-1.0);
typedef int64_t ll;
struct lenka
{map<int,vector<int>> ma;map<int,map<int,int>> idx;void push(int a,int b,int c){ma[a-b].push_back(c-a);}void uniq(){for(auto &kv:ma){auto &v=kv.se;v.push_back(-MAX);v.push_back(0);sort(v.begin(),v.end());v.erase(unique(v.begin(),v.end()),v.end());for(int i=0;i<sz(v);i++)idx[kv.fi][v[i]]=i;fill(v.begin(),v.end(),0);}}void add(int a,int b,int c,int y){auto &v=ma[a-b];int x=idx[a-b][c-a];while(x+1<=sz(v)){v[x]+=y;v[x]%=MOD;x+=x&(-x);}}int ask(int a,int b,int c){auto &v=ma[a-b];int x=idx[a-b][c-a]-1;int sum=0;while(x>0)(sum+=v[x])%=MOD,x-=x&(-x);return sum;}
}v[3];
char s[MAX];
int solve()
{scanf("%s",s);int n=strlen(s);int sc=0,si=0,sp=0;for(int i=0;i<n;i++){sc+=s[i]=='C';si+=s[i]=='I';sp+=s[i]=='P';v[0].push(si,sp,sc);v[1].push(sc,sp,si);v[2].push(sc,si,sp);}for(int i=0;i<3;i++){v[i].uniq();v[i].add(0,0,0,1);}sc=si=sp=0;for(int i=0;i<n;i++){sc+=s[i]=='C';si+=s[i]=='I';sp+=s[i]=='P';int ac=v[0].ask(si,sp,sc);int ai=v[1].ask(sc,sp,si);int ap=v[2].ask(sc,si,sp);int sum=((ac+ai)%MOD+ap)%MOD;if(i+1==n)return printf("%d\n",sum);v[0].add(si,sp,sc,sum);v[1].add(sc,sp,si,sum);v[2].add(sc,si,sp,sum);}return 0;
}
int main()
{int T=1;//cin>>T;while(T--)solve();return 0;
}

F. Make a Loop

思路:因为最后所有弧要连接闭合,那么最终这些圆弧的向量和一定为0。我们可以依次将所有弧区分为4个部分,如下图:
在这里插入图片描述
假设图中起点为 S S S,方向为逆时针方向。绿色部分圆弧向量为 ( − r i , − r i ) (-r_i,-r_i) (ri,ri),半径总和为 S 1 S_1 S1。黄色部分向量为 ( r i , − r i ) (r_i,-r_i) (ri,ri),半径总和为 S 2 S_2 S2。蓝色部分向量为 ( r i , r i ) (r_i,r_i) (ri,ri),半径总和为 S 3 S_3 S3。红色部分向量为 ( − r i , r i ) (-r_i,r_i) (ri,ri),半径总和为 S 4 S_4 S4。由最终向量和为0得 S 1 + S 4 = S 2 + S 3 S 1 + S 2 = S 4 + S 3 S 1 + S 2 + S 4 + S 3 = ∑ 1 ≤ i ≤ n r i S_1+S_4=S_2+S_3\\ S_1+S_2=S_4+S_3\\ S_1+S_2+S_4+S_3=\sum_{1\le i\le n} r_i S1+S4=S2+S3S1+S2=S4+S3S1+S2+S4+S3=1inri由上面的式子可得 S 1 + S 4 = S 2 + S 3 = S 1 + S 2 = S 3 + S 4 = ∑ r i 2 S_1+S_4=S_2+S_3=S_1+S_2=S_3+S_4=\frac{\sum r_i}2 S1+S4=S2+S3=S1+S2=S3+S4=2ri,同时因为最终所有弧会链接闭合,角度和为360°,所以4个部分里圆弧个数的奇偶性一定相等,那么任意2个部分的圆弧个数和一定是偶数。
综上所述,如果存在至少4种方法,使得我们从所有圆弧中选出偶数个圆弧的半径和,为总半径和的一半时,那我们一定有办法将所有圆弧首尾相连形成闭环。
采用dp, d [ 0 / 1 ] [ s ] d[0/1][s] d[0/1][s]表示选择偶数 / / /奇数个弧且半径和为 s s s的方案数,当 d [ 0 ] [ ∑ r i 2 ] ≥ 4 d[0][\frac{\sum r_i}2]\ge4 d[0][2ri]4时为Yes。复杂度 O ( n ∑ r i ) O(n\sum r_i) O(nri)

#include<bits/stdc++.h>
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<ll,ll>
using namespace std;
const int MAX=500010;
const int MOD=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
int d[2][MAX];
int solve()
{int n;cin>>n;vector<int>a;a.resize(n);for(int i=0;i<n;i++)scanf("%d",&a[i]);int m=accumulate(a.begin(),a.end(),0);if(m%2)return puts("No");memset(d,0,sizeof d);d[0][0]=1;m/=2;for(int i=0;i<n;i++)for(int j=m;j>=a[i];j--){d[0][j]=min(d[0][j]+d[1][j-a[i]],4);d[1][j]=min(d[1][j]+d[0][j-a[i]],4);}return puts(d[0][m]==4?"Yes":"No");
}
int main()
{int T=1;
//    cin>>T;while(T--)solve();return 0;
}

G. Remodeling the Dungeon

思路:图为一棵树,入口和出口之间只有一条唯一路径,以入口为根节点建图,那我们只需要从出口开始枚举删除出口至入口路径上的边,删一条边会将图分割为底部和根2棵子树,因为是从底部向根枚举,所以底部每次都会新加入一些点,在删边的时候,我们只需要枚举这些新加入的点对答案的贡献即可。复杂度 O ( h ∗ w ) O(h*w) O(hw)

#include<bits/stdc++.h>
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<int,int>
#define bit bitset<100000>
using namespace std;
const int MAX=3e5+10;
const int MOD=998244353; //G=3
const int INF=INT_MAX;
const double PI=acos(-1.0);
typedef int64_t ll;
vector<int>e[MAX];
vector<int>wall[MAX];
int fa[MAX],vis[MAX],d[MAX];
void dfs(int k,int f)
{d[k]=d[f]+1;fa[k]=f;vis[k]=1;for(int son:e[k])if(son!=f)dfs(son,k);
}
void get(int k,int from, vector<int> &v)
{vis[k]=0;v.push_back(k);for(int son:e[k]){if(son==fa[k]||son==from)continue;get(son,from,v);}
}
int ans=0;
void cal(int k,int from,int dep)
{if(k==0)return;vector<int>v;get(k,from,v);for(int i:v)for(int j:wall[i]){if(vis[j]==0)continue;ans=max(ans,d[j]+d[i]-d[k]+dep-d[k]+1);}cal(fa[k],k,dep);
}
int solve()
{int n,m;cin>>n>>m;vector<string>s;s.resize(2*n+1);for(string &i:s)cin>>i;auto id=[m](int x,int y){return ((x-1)/2)*m+(y-1)/2+1;};int h=2*n+1;int w=2*m+1;memset(wall,0,sizeof wall);for(int i=1;i<h;i+=2)for(int j=1;j<w;j+=2){if(j+2<w){int x=id(i,j),y=id(i,j+2);if(s[i][j+1]=='.'){e[x].push_back(y);e[y].push_back(x);}else{wall[x].push_back(y);wall[y].push_back(x);}}if(i+2<h){int x=id(i,j),y=id(i+2,j);if(s[i+1][j]=='.'){e[x].push_back(y);e[y].push_back(x);}else{wall[x].push_back(y);wall[y].push_back(x);}}}memset(d,0,sizeof d);memset(fa,0,sizeof fa);memset(vis,0,sizeof vis);dfs(1,0);ans=d[n*m];cal(n*m,0,d[n*m]);return printf("%d\n",ans);
}
int main()
{int T=1;//cin>>T;while(T--)solve();return 0;
}

H. Cake Decoration

思路:考虑构造四个数 a , b , c , d a,b,c,d a,b,c,d  并使  a < b < c < d a<b<c<d a<b<c<d。我们枚举 a , b a,b a,b,可以得到 c ∗ d c*d cd的值为 s = X a ∗ b s=\frac X{a*b} s=abX,要满足  b < c < d b<c<d b<c<d 那么 c c c的值域即为 [ b + 1 , ⌊ s ⌋ ] [b+1,\lfloor\sqrt s\rfloor] [b+1,s ⌋]。然后考虑计算以下6情况的方案数

  1. L ≤ a + b < R L\le a+b\lt R La+b<R
  2. L ≤ a + c < R L\le a+c\lt R La+c<R
  3. L ≤ a + d < R L\le a+d\lt R La+d<R
  4. L ≤ b + c < R L\le b+c\lt R Lb+c<R
  5. L ≤ b + d < R L\le b+d\lt R Lb+d<R
  6. L ≤ c + d < R L\le c+d\lt R Lc+d<R

其中1-5种方案均包含 a , b a,b a,b,我们可以通过 L , R , a , b L,R,a,b L,R,a,b反推出方案数。
第6种方案只知道 c c c的值域,因为 c < d c<d c<d 且  c + d c+d c+d 的和满足单调性,可以用二分求出上下界得出方案数。枚举 a , b a,b a,b复杂度 O ( X ) O(\sqrt X) O(X ),二分复杂度 O ( log ⁡ 2 X ) O(\log_2X) O(log2X),总复杂度 O ( X log ⁡ 2 X ) O(\sqrt X\log_2X) O(X log2X)

#include<bits/stdc++.h>
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<ll,ll>
#define bit bitset<100000>
using namespace std;
const int MAX=1e7+10;
const int MOD=998244353; //G=3
const int INF=INT_MAX/2;
const double PI=acos(-1.0);
typedef int64_t ll;
void add(ll &x,ll y){x=(x+max(0ll,y%MOD))%MOD;}
ll cal(ll X,ll R)
{R--;ll ans=0;for(ll a=1;a*(a+1)*(a+2)*(a+3)<=X;a++)for(ll b=a+1;a*b*(b+1)*(b+2)<=X;b++){ll s=X/a/b;ll m=sqrt(s);while(m*(m+1)<=s)m++;while(m*(m+1)>s)m--;if(m<=b)continue;if(a+b<=R)add(ans,m-b);//a,badd(ans,min(m,R-a)-b); //a,cadd(ans,min(m,R-b)-b); //b,cif(a<R){ll l=max(b+1,s/(R-a));l+=(s/l+a>R);add(ans,m-l+1); //a,d}if(b<R){ll l=max(b+1,s/(R-b));l+=(s/l+b>R);add(ans,m-l+1); //b,d}ll l=b+1,r=m,down=m+1;while(r>=l){ll x=mid,y=s/mid;if(x+y>R)l=mid+1;else down=mid,r=mid-1;}add(ans,m-down+1);// c,d}return 4*ans%MOD;
}
int solve()
{ll X,L,R;cin>>X>>L>>R;return printf("%lld\n",(cal(X,R)-cal(X,L)+MOD)%MOD);
}
int main()
{int T=1;//cin>>T;while(T--)solve();return 0;
}

I. Quiz Contest

思路:先按照正常思路解题,然后考虑优化。依次遍历第 i i i个competitor,假设该选手在第 k k k题获胜,那么第 i i i个选手获胜的方案数为 A k − b i ∗ ( k − 1 ) ! ∗ b i ∗ C a i b i ∗ ( m − 1 − k ) ! \red{A_{k-b_i}*(k-1)!}*b_i*C_{a_i}^{bi}* \green{(m-1-k)!} Akbi(k1)!biCaibi(m1k)!其中红色部分式子代表 [ 1 , k − 1 ] [1,k-1] [1,k1]题的排列数,中间黑色式子代表第 k k k题的排列数,绿色部分代表 [ k + 1 , m ] [k+1,m] [k+1,m]题目的排列数。
A k − b i \red{A_{k-b_i}} Akbi代表从除 a i a_i ai之外的其余题目中选出 k − b i k-b_i kbi个题目,且其他选手均不会比 i i i提前获胜的方案数(其他选手不可以提前答对所有 b b b个题目)。那么 A A A就需要dp来求了,用 d [ j ] [ g ] d[j][g] d[j][g]表示从前 j j j个选手的题目中选出 g g g个题目的合法方案数,转移方程如下: d j , g = ∑ p = 0 b j − 1 d j − 1 , g − p ∗ C a j p d_{j,g}=\sum_{p=0}^{b_j-1} d_{j-1,g-p}*C_{a_j}^p dj,g=p=0bj1dj1,gpCajp知道了在第 k k k题获胜的方案数后,那么选手 i i i获胜的总方案数为 = ∑ k = b i A k − b i ∗ ( k − 1 ) ! b i ∗ C a i b i ∗ ( m − 1 − k ) ! = b i ∗ C a i b i ∗ ∑ k = b i A k − b i ∗ ( k − 1 ) ! ∗ ( m − 1 − k ) ! \begin{aligned}&=\sum_{k=b_i}A_{k-b_i}*(k-1)!b_i*C_{a_i}^{bi}* (m-1-k)!\\ &=b_i*C_{a_i}^{bi}*\sum_{k=b_i}A_{k-b_i}*(k-1)!*(m-1-k)!\end{aligned} =k=biAkbi(k1)!biCaibi(m1k)!=biCaibik=biAkbi(k1)!(m1k)!其中 d p dp dp复杂度是 O ( n ∗ m ∗ b j ) O(n*m*b_j) O(nmbj),后面统计答案还需要遍历 k k k,复杂度 O ( n ∗ k ) O(n*k) O(nk),无法通过。
观察上面2个式子,均满足卷积性质,于是我们可以先通过分治NTT,预处理出 A A A,并记录分治的中间结果(因为 ∑ b i ≤ ∑ a i = m \sum b_i\le \sum a_i=m biai=m,所以时间和空间复杂度均为 O ( m log ⁡ 2 2 ( m ) ) O\big(m\log_2^2(m)\big) O(mlog22(m))
预处理出 A A A的分治结果后,再第二次通过分治,利用NTT结合第一次分治处理的 A A A结果,依次计算出每个玩家 i i i的获胜总方案数,因为这次是从上至下计算, A A A的结果数量是逐渐减小的,所以每次NTT计算出来的结果有很多对一下层都是无用的,可以优化删掉,复杂度 O ( m log ⁡ 2 2 ( m ) ) O\big(m\log_2^2(m)\big) O(mlog22(m))

#include<bits/stdc++.h>
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<ll,ll>
#define bit bitset<100000>
using namespace std;
const int MAX=2e6+10;
const int MOD=998244353; //G=3
const int INF=INT_MAX/2;
const double PI=acos(-1.0);
typedef int64_t ll;
ll POW(ll x,ll n)
{ll ret=1;while(n){if(n&1)ret=ret*x%MOD;x=x*x%MOD;n>>=1;}return ret;
}
void NTT(vector<ll>& p,ll len,int tag)
{auto rev=[](ll x,ll len){ll ret=0;for(ll i=0;(1<<i)<len;i++){ret<<=1;if(x&(1<<i))ret|=1;}return ret;};for(int i=0;i<len;i++){ll x=rev(i,len);if(i<x)swap(p[i],p[x]);}for(int i=1;i<len;i<<=1){ll wn=POW(3,(MOD-1)/(2*i));if(tag==-1)wn=POW(wn,MOD-2);for(int j=0;j<len;j+=i*2){ll w=1;for(int k=0;k<i;k++){ll x=p[j+k];ll y=w*p[j+k+i]%MOD;p[j+k]=(x+y)%MOD;p[j+k+i]=(x-y+MOD)%MOD;w=w*wn%MOD;}}}if(tag==-1)for(int i=0,x=POW(len,MOD-2);i<len;i++)p[i]=p[i]*x%MOD;
}
vector<ll> mul(vector<ll> a,vector<ll> b)
{int n=a.size();int m=b.size();ll len=1;while(len<n+m)len*=2;a.resize(len);b.resize(len);NTT(a,len,1);NTT(b,len,1);for(int i=0;i<len;i++)a[i]=a[i]*b[i]%MOD;NTT(a,len,-1);a.resize(n+m-1);return a;
}
ll fac[MAX],inv[MAX];
ll C(int n,int m){return fac[n]*inv[m]%MOD*inv[n-m]%MOD;}
ll a[MAX],b[MAX];
vector<ll>A[MAX];
void init(int k,int l,int r) //预处理出dp结果
{if(l==r){for(int i=0;i<b[r];i++)A[k].push_back(C(a[r],i));return;}init(lson,l,mid);init(rson,mid+1,r);A[k]=mul(A[lson],A[rson]);
}
vector<ll>ans;
void cal(int k,int l,int r,const vector<ll> &p)
{if(l==r)return ans.push_back(C(a[r],b[r])%MOD*b[r]%MOD*p[b[r]-1]%MOD);vector<ll> L=p,R=p;reverse(A[lson].begin(),A[lson].end());reverse(A[rson].begin(),A[rson].end());L=mul(L,A[rson]);R=mul(R,A[lson]);L.erase(L.begin(),L.begin()+A[rson].size()-1);//删除无用结果R.erase(R.begin(),R.begin()+A[lson].size()-1);L.resize(A[lson].size()); //精简优化结果R.resize(A[rson].size());cal(lson,l,mid,L);cal(rson,mid+1,r,R);
}
int solve()
{int n,m;cin>>n>>m;fac[0]=inv[0]=inv[1]=1;for(int i=1;i<=m;i++)fac[i]=fac[i-1]*i%MOD;for(int i=2;i<=m;i++)inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;for(int i=1;i<=m;i++)inv[i]=inv[i]*inv[i-1]%MOD;for(int i=1;i<=n;i++)scanf("%lld",a+i);for(int i=1;i<=n;i++)scanf("%lld",b+i);init(1,1,n);vector<ll>p;for(int i=0;i<m;i++)p.push_back(fac[i]*fac[m-1-i]%MOD);cal(1,1,n,p);for(ll i:ans)printf("%lld\n",i);return 0;
}
int main()
{int T=1;//cin>>T;while(T--)solve();return 0;
}

J. Traveling Salesperson in an Island

题解

K. New Year Festival

思路:首先将所有 x x x坐标排序去重,可以想到最终状态所有事件会被分组,且每一组一定是由一件件首尾相接的事件组合而成,且该组事件的起点或终点一定是恰好位于某个 x i x_i xi上。
n ≤ 11 n\le11 n11,考虑状压dp,先预处理出

  1. l [ i ] [ S ] l[i][S] l[i][S],代表以 x i x_i xi为事件起点,事件状态为 S S S的一组事件的最小花费,转移方程为 l i , S = min ⁡ k + 2 j = S ( l i , k + l c o s t j ) l_{i,S}=\min_{k+2^j=S}(l_{i,k}+lcost_j) li,S=k+2j=Smin(li,k+lcostj)
  2. r [ i ] [ S ] r[i][S] r[i][S],代表以 x i x_i xi为事件终点,事件状态为 S S S的一组事件的最小花费,转移方程为 r i , S = min ⁡ k + 2 j = S ( r i , k + r c o s t j ) r_{i,S}=\min_{k+2^j=S}(r_{i,k}+rcost_j) ri,S=k+2j=Smin(ri,k+rcostj)
  3. f [ i ] [ j ] [ S ] f[i][j][S] f[i][j][S],代表在 [ x i , x j ] [x_i,x_j] [xi,xj]区间的两个端点处,安排状态为 S S S的所有事件的最小花费,转移方程为 f i , j , S = min ⁡ g + h = S ( l i , g + r j , h ) f_{i,j,S}=\min_{g+h=S}(l_{i,g}+r_{j,h}) fi,j,S=g+h=Smin(li,g+rj,h)

预处理好后,我们用 d [ i ] [ S ] d[i][S] d[i][S]代表在区间 [ x 0 , x i ] [x_0,x_i] [x0,xi]内安排好状态为 S S S的所有事件的最小花费,来进行最后的dp处理,转移方程如下: d j , S = min ⁡ g + h = S ( d i , g + f i , j , h ) d_{j,S}=\min_{g+h=S} (d_{i,g}+f_{i,j,h}) dj,S=g+h=Smin(di,g+fi,j,h)
因为事件的结束时间可以超出 x i x_i xi,所以最终答案为 a n s = min ⁡ g + h = 2 n − 1 d i , g + l i , h ans=\min_{g+h=2^n-1} d_{i,g}+l_{i,h} ans=g+h=2n1mindi,g+li,h
其中枚举子集复杂度为 O ( 3 n ) O(3^n) O(3n),枚举区间端点复杂度为 O ( ∑ 2 m i ) O(\sum^2 m_i) O(2mi),总复杂度 O ( 3 n ∗ ∑ 2 m i ) O(3^n*\sum^2 m_i) O(3n2mi)

#include<bits/stdc++.h>
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<ll,ll>
#define bit bitset<100000>
using namespace std;
const int MAX=2e6+10;
const int MOD=998244353; //G=3
const int INF=INT_MAX;
const double PI=acos(-1.0);
typedef int64_t ll;
void Min(ll &x,ll y,ll z)
{if(y<0||z<0)return;if(x<0)x=y+z;x=min(x,y+z);
}
struct lenka
{ll m,d;vector<pii>p;void read(vector<ll> &s){scanf("%lld%lld",&m,&d);p.resize(m);for(auto &kv:p)scanf("%lld%lld",&kv.fi,&kv.se);for(auto &kv:p)s.push_back(kv.fi);sort(s.begin(),s.end());s.erase(unique(s.begin(),s.end()),s.end());}ll cost(ll x){if(m==1)return x==p[0].fi?p[0].se:-1;for(int j=0;j+1<m;j++){ll k=(p[j+1].se-p[j].se)/(p[j+1].fi-p[j].fi);if(x>=p[j].fi&&x<=p[j+1].fi)return p[j].se+k*(x-p[j].fi);}return -1;}
}a[11];
ll d[60][1<<11];
ll f[60][1<<11];
ll l[60][1<<11];
ll r[60][1<<11];
ll sum[1<<11];
int solve()
{vector<ll>s;int n;cin>>n;for(int i=0;i<n;i++)a[i].read(s);memset(d,-1,sizeof d);memset(l,-1,sizeof l);memset(r,-1,sizeof r);memset(sum,0,sizeof sum);for(int i=0;i<(1<<n);i++)for(int j=0;j<n;j++){if((1<<j)&i)sum[i]+=a[j].d;}for(int i=0;i<sz(s);i++)d[i][0]=l[i][0]=r[i][0]=0;for(int i=0;i<sz(s);i++)for(int j=0;j<(1<<n);j++){ll len=0;for(int k=0;k<n;k++)if((1<<k)&j)len+=a[k].d;for(int k=0;k<n;k++){if((1<<k)&j)continue;Min(l[i][j+(1<<k)],l[i][j],a[k].cost(s[i]+len));Min(r[i][j+(1<<k)],r[i][j],a[k].cost(s[i]-len-a[k].d));}}for(int i=0;i<sz(s);i++){memset(f,-1,sizeof f);for(int j=i;j<sz(s);j++)f[j][0]=0;for(int j=i;j<sz(s);j++)for(int k=0;k<(1<<n);k++){int S=(1<<n)-1-k;for(int h=S;h;h=(h-1)&S){if(s[j]-s[i]<sum[k+h])continue;Min(f[j][k+h],l[i][k],r[j][h]);}if(s[j]-s[i]>=sum[k])Min(f[j][k],l[i][k],r[j][0]);}for(int j=i;j<sz(s);j++)for(int k=0;k<(1<<n);k++){int S=(1<<n)-1-k;for(int h=S;h;h=(h-1)&S)Min(d[j][k+h],d[i][k],f[j][h]);Min(d[j][k],d[i][k],f[j][0]);}}ll ans=-1;for(int i=0;i<sz(s);i++)for(int j=0;j<(1<<n);j++)Min(ans,d[i][j],l[i][(1<<n)-1-j]);return printf("%lld\n",ans);
}
int main()
{int T=1;//cin>>T;while(T--)solve();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/612398.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【申请SSL证书】免费申请阿里云SSL证书

注意&#xff1a;申请 SSL证书的前提是有一个域名且备案了 第一部&#xff1a;申请免费证书 免费 CA 证书购买地址&#xff08;请戳这里&#xff09; 选择合适的选项如下图 为了解决免费证书近期存在的吊销、统计等问题&#xff0c;自2021年起&#xff0c;免费证书申请申请将…

鸿蒙Harmony--状态管理器--@Prop详解

纵横千里独行客&#xff0c;何惧前路雨潇潇。夜半浊酒慰寂寞&#xff0c;天明走马入红尘。且将新火试新茶&#xff0c;诗酒趁年华。青春以末&#xff0c;壮志照旧&#xff0c;生活以悟&#xff0c;前路未明。时间善变&#xff0c;可执着翻不了篇。时光磨我少年心&#xff0c;却…

YOLOv5改进 | 检测头篇 | DynamicHead支持检测和分割(不同于网上版本,全网首发)

一、本文介绍 本文给大家带来的改进机制是DynamicHead(Dyhead),这个检测头由微软提出的一种名为“动态头”的新型检测头,用于统一尺度感知、空间感知和任务感知。网络上关于该检测头我查了一些有一些魔改的版本,但是我觉得其已经改变了该检测头的本质,因为往往一些细节上才…

【读书笔记】学习突围

最近在读一本书《学习突围》&#xff0c;作者是常青&#xff0c;知乎大V。对他的一些回答非常认同&#xff0c;受益匪浅&#xff0c;特此买来纸质书籍细细学习一番&#xff01; 1.【学习心态】&#xff08;拖延症、自控、执行力、专注力&#xff09; 2.【学习方法】&#xff0…

2024.1.11每日一题

LeetCode 2645.构造有效字符串的最少插入数 2645. 构造有效字符串的最少插入数 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你一个字符串 word &#xff0c;你可以向其中任何位置插入 “a”、“b” 或 “c” 任意次&#xff0c;返回使 word 有效 需要插入的最少字…

Java Http各个请求类型详细介绍

1. 前言 在Spring Boot框架中&#xff0c;HTTP请求类型是构建Web应用程序的重要组成部分。常见的请求类型包括GET、POST、PUT和DELETE&#xff0c;每种类型都有其特定的用途和特点。本文将详细比较这四种请求类型&#xff0c;帮助您在开发过程中做出明智的选择。 2. GET请求…

order by之后的injection(sqllabs第四十六关)

order by相关注入知识 这一关的sql语句是利用的order by 根据输入的id不同数据排序不一样可以确定就是order by order by后面无法使用ubion注入&#xff08;靠找不到&#xff09; 可以利用后面的参数进行攻击 1&#xff09;数字 没作用考虑布尔类型 rand和select ***都可以 …

Linux_Centos7安装snmp服务

Linux_Centos7安装snmp服务 1.背景2.目的3.环境4.操作4.1 手动安装snmp协议4.2 批量安装snmp协议 1.背景 收到云平台异常告警&#xff0c;提示ECS服务器在在一分钟内客户端存在多次ssh远程登陆&#xff0c;被判断为ssh远程破解&#xff0c;通过排查得出为运维系统配置过程中错…

【算法】最佳牛围栏(二分,前缀和,双指针)

题目 农夫约翰的农场由 N 块田地组成&#xff0c;每块地里都有一定数量的牛&#xff0c;其数量不会少于 1 头&#xff0c;也不会超过 2000 头。 约翰希望用围栏将一部分连续的田地围起来&#xff0c;并使得围起来的区域内每块地包含的牛的数量的平均值达到最大。 围起区域内…

2023年山东省职业院校技能大赛高职组信息安全管理与评估 理论题(正式赛)

2023年山东省职业院校技能大赛高职组信息安全管理与评估 理论题 理论技能与职业素养&#xff08;100分&#xff09; 2023年山东省职业院校技能大赛高职组信息安全管理与评估 理论题 【注意事项】 Geek极安云科专注技能竞赛技术提升&#xff0c;基于各大赛项提供全面的系统性…

LeNet-5(fashion-mnist)

文章目录 前言LeNet模型训练 前言 LeNet是最早发布的卷积神经网络之一。该模型被提出用于识别图像中的手写数字。 LeNet LeNet-5由以下两个部分组成 卷积编码器&#xff08;2&#xff09;全连接层&#xff08;3&#xff09; 卷积块由一个卷积层、一个sigmoid激活函数和一个…

了解结构体以及结构体数组

C语言的结构体你真的了解吗&#xff1f; 一起来看一下吧&#xff01;&#xff01;&#xff01; 1.结构体是啥&#xff1f; 结构体是多种数据类型的组合体 2.格式&#xff08;一般放在主函数前&#xff0c;也就是int main()前面 &#xff09; 关键字 结构体名字 {成员列表…

python代码练习:双指针法

题目一&#xff1a;移除元素 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不…

[足式机器人]Part3 机构运动学与动力学分析与建模 Ch00-2(1) 质量刚体的在坐标系下运动

本文仅供学习使用&#xff0c;总结很多本现有讲述运动学或动力学书籍后的总结&#xff0c;从矢量的角度进行分析&#xff0c;方法比较传统&#xff0c;但更易理解&#xff0c;并且现有的看似抽象方法&#xff0c;两者本质上并无不同。 2024年底本人学位论文发表后方可摘抄 若有…

【论文阅读】Deep Graph Infomax

目录 0、基本信息1、研究动机2、创新点2.1、核心思想&#xff1a;2.2、思想推导&#xff1a; 3、准备3.1、符号3.2、互信息3.3、JS散度3.4、Deep InfoMax方法3.5、判别器&#xff1a;f-GAN估计散度 4、具体实现4.1、局部-全局互信息最大化4.2、理论动机 5、实验设置5.1、直推式…

C# 使用Fleck创建WebSocket服务器

目录 写在前面 代码实现 服务端代码 客户端代码 调用示例 写在前面 Fleck 是 C# 实现的 WebSocket 服务器&#xff0c;通过 WebSocket API&#xff0c;浏览器和服务器只需要做一个握手的动作&#xff0c;然后浏览器和服务器之间就形成了一条快速通道&#xff1b;两者之间…

1.5 Unity中的数据存储 PlayerPrefs

Unity中的三种数据存储&#xff1a;数据存储也称为数据持久化 一、PlayerPrefs PlayerPrefs是Unity引擎自身提供的一个用于本地持久化保存与读取的类&#xff0c;以键值对的形式将数据保存在文件中&#xff0c;然后程序可以根据关键字提取数值。 PlayerPrefs类支持3种数据类…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《计及储能参与的电能-调频-备用市场日前联合交易决策模型》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 这个标题涉及到电能、调频和备用市场的联合交易决策模型&#xff0c;并特别考虑了储能在其中的参与。 电能市场&#xff1a; 这是指电能的买卖市场&…

Java使用IText生产PDF时,中文标点符号出现在行首的问题处理

Java使用IText生成PDF时&#xff0c;中文标点符号出现在行首的问题处理 使用itext 5进行html转成pdf时&#xff0c;标点符号出现在某一行的开头 但这种情况下显然不符合中文书写的规则&#xff0c;主要问题出在itext中的DefaultSplitCharacter类&#xff0c;该方法主要用来判断…

04- OpenCV:Mat对象简介和使用

目录 1、Mat对象与IplImage对象 2、Mat对象使用 3、Mat定义数组 4、相关的代码演示 1、Mat对象与IplImage对象 先看看Mat对象&#xff1a;图片在计算机眼里都是一个二维数组&#xff1b; 在OpenCV中&#xff0c;Mat是一个非常重要的类&#xff0c;用于表示图像或矩阵数据。…