ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

2022“杭电杯”中国大学生算法设计超级联赛(3)

2022-07-28 04:00:06  阅读:118  来源: 互联网

标签:联赛 int LL long while IL 2022 杭电杯 define


链接

\(Equipment Upgrade\)

令 \(f_i\) 为升到 \(i\) 级所需期望费用,易得:
\(f_i = f_{i-1} + c_{i-1} + \frac{1-p_{i-1}}{\sum\limits_{j=1}^{i-1}w_j}\sum\limits_{j=0}^{i-1}(f_i-f_j)w_{i-j-1}\),
再化化得 \(p_{i-1}f_i=f_{i-1}+c_{i-1}-\frac{1-p_{i-1}}{\sum\limits_{j=1}^{i-1}}\sum\limits_{j=0}^{i-1}f_j w_{i-j-1}\) 。
很明显得卷积形式,分治 \(FFT\) 即可。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=32e4+15,p=998244353,G=3,Gi=332748118;
int n,P[N],w[N],f[N],pw[N],g[N],c[N],a[N],r[N];
int _a[N],_b[N],_c[N];
IL LL in(){
	char c;int f=1;
	while((c=getchar())<'0'||c>'9')
	  if(c=='-') f=-1;
	LL x=c-'0';
	while((c=getchar())>='0'&&c<='9')
	  x=x*10+c-'0';
	return x*f;
}
IL int mod(int x){return x>=p?x-p:x;}
IL int ksm(int a,int b){
	int c=1;
	while(b){
		if(b&1) c=1ll*c*a%p;
		a=1ll*a*a%p,b>>=1;
	}
	return c;
}
IL void calc(int lim){
	for(int i=0;i<lim;++i)
	  r[i]=(r[i>>1]>>1)|((i&1)*(lim>>1));
}
IL void NTT(int *a,int lim,int op){
	calc(lim);
	for(int i=0;i<lim;++i)
	  if(i<r[i]) swap(a[i],a[r[i]]);
	for(int i=1;i<lim;i<<=1){
		int wn=ksm(~op?G:Gi,(p-1)/(i<<1));
		for(int j=0;j<lim;j+=i<<1){
			int w=1;
			for(int k=0;k<i;++k,w=1ll*w*wn%p){
				int x=a[j+k],y=1ll*w*a[j+i+k]%p;
				a[j+k]=mod(x+y),a[j+i+k]=mod(x-y+p);
			}
		}
	}
	if(op==-1){
		int inv=ksm(lim,p-2);
		for(int i=0;i<lim;++i) a[i]=1ll*a[i]*inv%p;
	}
}
IL void mul(int *a,int *b,int *c,int n,int m){
	int lim=1;
	while(lim<n+m-1) lim<<=1;
	memcpy(_a,a,4*n),memcpy(_b,b,4*m),
	memset(_a+n,0,4*(lim-n)),memset(_b+m,0,4*(lim-m));
	NTT(_a,lim,1),NTT(_b,lim,1);
	for(int i=0;i<lim;++i) _c[i]=1ll*_a[i]*_b[i]%p;
	NTT(_c,lim,-1);
	for(int i=0;i<n+m-1;++i) c[i]=_c[i];
}
void fenzhi(int l,int r){
	if(l==r){
		if(l){
			f[l]=mod(1ll*mod(1-P[l-1]+p)*pw[l-1]%p*f[l]%p+mod(f[l-1]+c[l-1])),
			f[l]=1ll*f[l]*ksm(P[l-1],p-2)%p;	
		}
		return;
	}
	int mid=l+r>>1;
	fenzhi(l,mid);
	mul(f+l,w,a,mid-l+1,r-l+1);
	for(int i=mid+1;i<=r;++i) f[i]=mod(f[i]-a[i-l-1]+p);
	fenzhi(mid+1,r);
}
void solve(){
	n=in();int i100=ksm(100,p-2);
	memset(f,0,8*n);
	for(int i=0;i<n;++i) P[i]=1ll*in()*i100%p,c[i]=in();
	for(int i=1;i<n;++i) w[i]=in(),pw[i]=mod(pw[i-1]+w[i]);
	for(int i=1;i<n;++i) pw[i]=ksm(pw[i],p-2);
	fenzhi(0,n);
	printf("%d\n",f[n]);
}
int main()
{
	int T=in();
	while(T--) solve();
	return 0;
}

\(Boss Rush\)

当确定 \(t\) ,判断可行性时我们发现每个技能的贡献很好计算,于是乎我们二分答案。
\(n\) 很小,我们可以直接枚举集合状压 \(dp\) 判断。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=18,M=19e5+3;
struct hh{
    int t,len;vector<LL>d;
}a[N];
int n,t[1<<N],mp[M];
LL H,f[1<<N];
IL LL in(){
    char c;int f=1;
    while((c=getchar())<'0'||c>'9')
      if(c=='-') f=-1;
    LL x=c-'0';
    while((c=getchar())>='0'&&c<='9')
      x=x*10+c-'0';
    return x*f;
}
IL int lb(int x){return x&-x;}
IL void init(){
    for(int i=0,j=1;i<=n;++i,j<<=1) mp[j]=i;
    for(int i=1;i<(1<<n);++i) t[i]=t[i^lb(i)]+a[mp[lb(i)]].t;
}
IL LL get(int tim,int k){
    if(a[k].len-1<tim) return a[k].d[a[k].len-1];
    return a[k].d[tim];
}
IL int pd(int tim){
    memset(f,0,8*(1<<n)),f[0]=0;
    for(int i=1;i<(1<<n);++i){
        for(int j=0;j<n;++j)
          if(i>>j&1){
              if(t[i^1<<j]<=tim) f[i]=max(f[i],f[i^1<<j]+get(tim-t[i^1<<j],j));
          }
        if(f[i]>=H) return 1;
    }
    return 0;
}
void solve(){
    n=in(),H=in();
    for(int i=0;i<n;++i){
        a[i].t=in(),a[i].len=in();
        a[i].d.clear();
        for(int j=0;j<a[i].len;++j) a[i].d.push_back(in());
        for(int j=1;j<a[i].len;++j) a[i].d[j]+=a[i].d[j-1];
    }
    init();
    int l=0,r=M;
    while(l<r){
        int mid=l+r>>1;
        if(pd(mid)) r=mid;
        else l=mid+1;
    }
    if(pd(l)) printf("%d\n",l);
    else printf("-1\n");
}
int main()
{
    int T=in();
    while(T--) solve();
    return 0;
}

\(Cyber Language\)

我有一个绝妙的方法,可惜这里写不下。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=55;
char s[N];
IL LL in(){
    char c;int f=1;
    while((c=getchar())<'0'||c>'9')
      if(c=='-') f=-1;
    LL x=c-'0';
    while((c=getchar())>='0'&&c<='9')
      x=x*10+c-'0';
    return x*f;
}
void solve(){
    gets(s+1);
    int n=strlen(s+1);
    putchar(s[1]-'a'+'A');
    for(int i=2;i<=n;++i)
      if(s[i]>='a'&&s[i]<='z'&&s[i-1]==' ') putchar(s[i]-'a'+'A');
    putchar('\n');
}
int main()
{
    int T=in();
    while(T--) solve();
    return 0;
}

\(Spanning Tree Game\)

题解十分巧妙啊qwq
我们肯定还是将各种边从小到大排序进行 \(dp\) 。
对于同一条边,排在后面的边权大的边需要看边权小的边的脸色行事,直接 \(dp\) 状态数爆炸。
我们枚举到后面的边时,如果两个点不连通,那我们显然就可以知道前面的边没选。
那连通了呢?选不选这条边不就对答案没有影响吗?
唯一的问题是是否选这条边会影响 \(a\) 边的数量,在不知道前面边的状态的情况下瞎选可不行。
得找个办法使这两条边对 \(a\) 边数量的影响在第一条边时就确定。
若 \(a<b\) ,我们将边拆成 \((x,y,a,1)\) 和 \((x,y,b,0)\)
若 \(a \geq b\) ,我们将边拆成 \((x,y,b,-1)\) 和 \((x,y,a,0)\) 。
初始时认为加入了第二种情况数量的 \(a\) 边即可。
考虑 \(dp\),设 \(f_{i,S,j}\) 表示枚举到第 \(i\) 条边,点的连通情况时 \(S\) ,且加入了 \(j\) 条 \(a\) 边的最大值,预处理可 \(O(1)\) 转移。
对于 \(S\),我们将其压缩为一个 \(long long\) 。具体的,我们将每个点的值设为其所处连通块中序号最小的点,\(n \leq 9\) ,所以我们要用 \(4\) 位表示一个数,\(36\) 位表示所有状态,存进 \(map\) 。
\(S\) 的状态数有 \(Bell(n)\) 个,其中 \(Bell(9)=21147\) 。

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=10,M=62,NN=22000,inf=2e9;
struct hh{
	int x,y,w,op;
	bool operator<(const hh &a) const{
	return w<a.w;}
}l[M];
int n,m,k,cnt,bo[N],f[M][NN][M],e[NN][N][N];
LL mp[NN],S,T;
unordered_map<LL,int>MP;
IL int in(){
	char c;int f=1;
	while((c=getchar())<'0'||c>'9')
	  if(c=='-') f=-1;
	int x=c-'0';
	while((c=getchar())>='0'&&c<='9')
	  x=x*10+c-'0';
	return x*f;
}
IL int get(LL x,int k){return (x>>(k*4-4))&((1<<4)-1);}
IL LL turn(LL x,int k){return x<<(k*4-4);}
void dfs(int k,LL s){
	if(k==n+1){MP[s]=++cnt,mp[cnt]=s;return;}
	for(int i=1;i<=n;++i)
	  if(bo[i]) dfs(k+1,s|turn(i,k));
	bo[k]=1,dfs(k+1,s|turn(k,k)),bo[k]=0;
}
void init(){
	for(int i=1;i<=cnt;++i){
		LL s=mp[i];
		for(int j=1;j<=n;++j)
		  for(int k=1;k<j;++k){
		  	int bj=get(s,j),bk=get(s,k);
		  	if(bj==bk) e[i][j][k]=e[i][k][j]=i;
		  	else{
		  		LL ss=0;
		  		if(bj>bk) swap(bj,bk);
		  		for(int ii=1;ii<=n;++ii)
		  		    if(get(s,ii)==bk) ss|=turn(bj,ii);
		  		    else ss|=turn(get(s,ii),ii);
		  		e[i][j][k]=e[i][k][j]=MP[ss];
		  	}
		  }
	}
}
void solve(){
	int x,y,va,vb;
	n=in(),m=in(),cnt=k=0,MP.clear(),dfs(1,0),init();
	for(int i=1;i<=m;++i){
		x=in(),y=in(),va=in(),vb=in();
		if(va<=vb){l[i*2-1]=(hh){x,y,va,1},l[i*2]=(hh){x,y,vb,0};}
		else{l[i*2-1]=(hh){x,y,va,0},l[i*2]=(hh){x,y,vb,-1},++k;}
	}
	m*=2;sort(l+1,l+m+1);S=T=0;
	for(int i=1;i<=n;++i) S|=turn(i,i),T|=turn(1,i);
	for(int i=0;i<=m;++i)
	  for(int j=1;j<=cnt;++j)
	    for(int k=0;k<=m;++k)
	      f[i][j][k]=-inf;
	f[0][MP[S]][k]=0;
	for(int i=0;i<m;++i)
	  for(int j=1;j<=cnt;++j)
	    for(int k=0;k<=(m>>1);++k){
	    	int x=l[i+1].x,y=l[i+1].y;LL s=mp[j];
	    	int bx=get(s,x),by=get(s,y);
	    	if(l[i+1].op){
	    		if(bx==by){
	    			if(k+l[i+1].op>=0) f[i+1][e[j][x][y]][k+l[i+1].op]=max(f[i+1][e[j][x][y]][k+l[i+1].op],f[i][j][k]);
	    			f[i+1][j][k]=max(f[i+1][j][k],f[i][j][k]);
				}
				else{
					if(k+l[i+1].op>=0) f[i+1][e[j][x][y]][k+l[i+1].op]=max(f[i+1][e[j][x][y]][k+l[i+1].op],f[i][j][k]+l[i+1].w);
					f[i+1][j][k]=max(f[i+1][j][k],f[i][j][k]);
				}
	    	}
	    	else{
	    		if(bx==by) f[i+1][j][k]=max(f[i+1][j][k],f[i][j][k]);
	    		else f[i+1][e[j][x][y]][k]=max(f[i+1][e[j][x][y]][k],f[i][j][k]+l[i+1].w);
	    	}
	    }
	for(int i=0;i<=(m>>1);++i) printf("%d\n",f[m][MP[T]][i]);
}
int main()
{
	int T=in();
	while(T--) solve();
	return 0;
}

\(Package Delivery\)

贪心,我们找到目前 \(r\) 最小的区间,把 \(l \leq r\) 的区间全塞入一个按 \(r\) 排序的小根堆里,每次取即弹出 \(k\) 个。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=1e5+3;
struct hh{
    int id,l,r;
    bool operator<(const hh &a) const{
    return r>a.r;}
}al[N],ar[N];
int n,k,vis[N];
bool cmp1(const hh &a,const hh &b){
    return a.l<b.l;
}
bool cmp2(const hh &a,const hh &b){
    return a.r<b.r;
}
priority_queue<hh>q;
IL LL in(){
    char c;int f=1;
    while((c=getchar())<'0'||c>'9')
      if(c=='-') f=-1;
    LL x=c-'0';
    while((c=getchar())>='0'&&c<='9')
      x=x*10+c-'0';
    return x*f;
}
void solve(){
    n=in(),k=in();
    memset(vis+1,0,4*n);
    while(q.size()) q.pop();
    for(int i=1;i<=n;++i)
      al[i]=(hh){i,in(),in()},ar[i]=al[i];
    sort(al+1,al+n+1,cmp1),sort(ar+1,ar+n+1,cmp2);
    int ans=0;
    for(int i=1,j=0;i<=n;++i)
      if(!vis[ar[i].id]){
          while(j<n&&al[j+1].l<=ar[i].r) q.push(al[++j]);
          ++ans;
          for(int cnt=1;cnt<=k&&q.size();) cnt+=!vis[q.top().id],vis[q.top().id]=1,q.pop();
            if(!vis[ar[i].id]) --i;  
      }
    printf("%d\n",ans);
}
int main()
{
    int T=in();
    while(T--) solve();
    return 0;
}

\(Taxi\)

开始觉得像天使玩偶,把询问与点按 \(x\) 排序,分治,再对 \(y\) 排序,按顺序塞进线段树中( \(w\) 的限制对索引稍加操作即可 ),复杂度 \(O(nlog^2n)\)。
但是,这道题是求最大值!
\(\max\{|x-x_i|+|y-y_i|\}\)

\(=\max\{\max \{ x-x_i,x_i-x \}+ \max \{y-y_i,y_i-y \} \}\)

\(=\max\{x+y-x_i-y_i,x-y-x_i+y_i,-x+y+x_i-y_i,-x-y+x_i+y_i\}\) 。

求最小值可不能酱紫鸭
我们只需求得那些点的
\(x_i+y_i,x_i-y_i,-x_i+y_i,-x_i-y_i\) 的最大值,即可 $ O(1) $ 求出距离的最大值。
我们考虑将点按 \(w\) 排序,预处理出上述值的后缀最大值。
随 \(i\) 的增大, \(w\) 递增, 后缀最大值递减,它们会存在一个交点,二分查找该交点位置即为答案。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=1e5+3,inf=2e9;
struct hh{
    int x,y,w;
    bool operator<(const hh &a) const{
    return w<a.w;}
    int operator*(const hh &a) const{
    return x*a.x+y*a.y;}
}a[N],op[4];
int n,m,Max[N][4],ans;
IL LL in(){
    char c;int f=1;
    while((c=getchar())<'0'||c>'9')
      if(c=='-') f=-1;
    LL x=c-'0';
    while((c=getchar())>='0'&&c<='9')
      x=x*10+c-'0';
    return x*f;
}
IL void ask(hh p){
    int l=1,r=n,ans=-2e9;
    while(l<=r){
        int mid=l+r>>1,val=-inf;
        for(int i=0;i<4;++i) val=max(val,Max[mid][i]+p*op[3-i]);
        if(val<=a[mid].w) ans=max(ans,val),r=mid-1;
        else ans=max(ans,a[mid].w),l=mid+1;
    }
    printf("%d\n",ans);
}
void solve(){
    n=in(),m=in();
    op[0]={1,1,0},op[1]={1,-1,0},op[2]={-1,1,0},op[3]={-1,-1,0};
    for(int i=1;i<=n;++i) a[i]=(hh){in(),in(),in()};
    sort(a+1,a+n+1);
    for(int i=0;i<4;++i) Max[n+1][i]=-inf;
    for(int i=n;i;--i)
        for(int j=0;j<4;++j)
          Max[i][j]=max(Max[i+1][j],a[i]*op[j]);
    while(m--) ask((hh){in(),in()});
}
int main()
{
    int T=in();
    while(T--) solve();
    return 0;
}

\(Two Permutations\)

想得到 \(S\) ,对 \(P,Q\) 的限制是很多的,我们要利用好这种限制。
因为 \(P,Q\) 皆为 \(1\) ~ \(n\) 的排列,我们用 \(f_{i,j}\) 表示目前到 \(P_i\) ,且 \(P_i\) 对应的是 \(S\) 中的第 \(j\) 次出现的位置,状态数为 \(O(n)\) 。
我们转移时也最多只有 \(2\) 个位置可以转移,而在其之间都需要 \(Q\) 去填补,预处理 \(hash\) ,\(O(1)\) 判断转移可行性即可。

#include<bits/stdc++.h>
#define IL inline
#define LL unsigned long long
using namespace std;
const int N=6e5+3,p=998244353;
int n,a[N],b[N],c[N],f[N][2],bo[N],pos[N][2];
struct hash{
    int P=37,n;LL f[N],w[N];
    void get_hash(int n,int *a){
        this->n=n;w[0]=1;
        for(int i=1;i<=n;++i)
          w[i]=w[i-1]*P,f[i]=f[i-1]*P+a[i];
    }
    IL LL split(int l,int r){
        return f[r]-f[l-1]*(w[r-l+1]);
    }
}A,B,C;
IL LL in(){
    char c;int f=1;
    while((c=getchar())<'0'||c>'9')
      if(c=='-') f=-1;
    LL x=c-'0';
    while((c=getchar())>='0'&&c<='9')
      x=x*10+c-'0';
    return x*f;
}
IL int mod(int x){return x>=p?x-p:x;}
void solve(){
    n=in();
    memset(bo+1,0,4*n);
    for(int i=1;i<=n;++i) a[i]=in(),pos[i][0]=pos[i][1]=f[i][0]=f[i][1]=0;
    for(int i=1;i<=n;++i) b[i]=in();
    for(int i=1;i<=2*n;++i) ++bo[c[i]=in()];
    for(int i=1;i<=n;++i)
      if(bo[i]^2){printf("0\n");return;}
    for(int i=1;i<=2*n;++i)
      if(pos[c[i]][0]) pos[c[i]][1]=i;
      else pos[c[i]][0]=i;
    B.get_hash(n,b),C.get_hash(2*n,c);
    f[0][0]=1;
    for(int i=0;i<n;++i)
      for(int j=0;j<=1;++j){
          int now=pos[a[i]][j],val=a[i+1],lb=now-i;
          if(pos[val][0]>now&&B.split(lb+1,lb+pos[val][0]-now-1)==C.split(now+1,pos[val][0]-1))
            f[i+1][0]=mod(f[i+1][0]+f[i][j]);
          if(pos[val][1]>now&&B.split(lb+1,lb+pos[val][1]-now-1)==C.split(now+1,pos[val][1]-1))
            f[i+1][1]=mod(f[i+1][1]+f[i][j]);
      }
    int ans=0;
    int now=pos[a[n]][0];
    if(now>=n&&B.split(now-n+1,n)==C.split(now+1,2*n)) ans=mod(ans+f[n][0]);
    now=pos[a[n]][1];
    if(now>=n&&B.split(now-n+1,n)==C.split(now+1,2*n)) ans=mod(ans+f[n][1]);
    printf("%d\n",ans);
}
int main()
{
    int T=in();
    while(T--) solve();
    return 0;
}

标签:联赛,int,LL,long,while,IL,2022,杭电杯,define
来源: https://www.cnblogs.com/yiqiAtiya/p/16527145.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有