Atcoder AGC019简要题解

传送门

Reverse and Compare

发现两个串如果翻转后一样且中心不一样,则必定有一个是回文串。然后就只用统计两端字母不同的串的个数了。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
 
const int N=2e5+50;
char ch[N];
int n,cnt[26],s;
LL ans;
int main() {
	scanf("%s",ch+1); n=strlen(ch+1);
	for(int i=1;i<=n;i++)
		ans+=s-cnt[ch[i]-'a'], ++s, ++cnt[ch[i]-'a'];
	cout<<ans+1<<'\n';
}

Fountain Walk

这道题想了半天,直到我看见每行每列最多一个喷泉。。。

然后就是求个最多能经过多少个了,也就是求个LIS。

#include <bits/stdc++.h>
using namespace std;
 
const int RLEN=1<<18|1;
inline char nc() {
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
	char ch=nc(); int i=0,f=1;
	while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
	while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc(); }
	return i*f;
}
 
const int N=2e5+50;
struct P {int x,y;} a[N];
int sx,sy,tx,ty,n,d[N],top;
int main() {
	sx=rd(), sy=rd(), tx=rd(), ty=rd(), n=rd();
	for(int i=1;i<=n;i++) {
		a[i].x=rd(), a[i].y=rd();
		if(a[i].x<min(sx,tx) || a[i].x>max(sx,tx) || a[i].y<min(sy,ty) || a[i].y>max(sy,ty)) --i, --n;
	}
	if(sx>tx) {
		sx=-sx; tx=-tx;
		for(int i=1;i<=n;i++) a[i].x=-a[i].x;
	}
	if(sy>ty) {
		sy=-sy; ty=-ty;
		for(int i=1;i<=n;i++) a[i].y=-a[i].y;
	}
	sort(a+1,a+n+1,[](const P &a,const P &b) {return a.x<b.x;});
	for(int i=1;i<=n;i++) {
		if((!top) || d[top]<a[i].y) d[++top]=a[i].y;
		else d[lower_bound(d+1,d+top+1,a[i].y)-d]=a[i].y;
	} 
	const double PI=acos(-1.0);
	double ans=(tx-sx+ty-sy)*100.0+top*(5.0*PI-20);
	if(top==min(tx-sx+1,ty-sy+1)) ans+=5.0*PI;
	printf("%.20f\n",ans);
}

Shift and Flip

枚举最后哪里开始相同,然后 O ( n log ⁡ n ) O(n \log n) O(nlogn)做个贪心,复杂度 O ( n 2 log ⁡ n ) O(n^2 \log n) O(n2logn)

#include <bits/stdc++.h>
using namespace std;
typedef pair <int,int> pii;
const int RLEN=1<<18|1;
inline char nc() {
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
	char ch=nc(); int i=0,f=1;
	while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
	while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
	return i*f;
}
 
const int N=2e3+50;
int n,pre[N],suf[N];
char a[N],b[N];
inline int dis(int a,int b) {
	if(a<=b) return b-a;
	else return b+n-a;
}
inline int solve(int p) {
	vector <pii> vec;
	for(int i=0;i<n;i++) {
		int tar=(i+p)%n;
		if(a[tar]^b[i]) 
			vec.push_back(pii(dis(pre[tar],tar),dis(tar,suf[tar])));
	}
	if(!vec.size()) return min(dis(0,p),dis(p,0));
	int ans=1e9;
	sort(vec.begin(),vec.end(),[](const pii &a,const pii &b) {return a.first<b.first || (a.first==b.first && a.second>b.second);});
	for(int i=vec.size()-1,mx=0;~i;i--) {
		mx=max(mx,vec[i].second);
		int mx2=(i==0) ? 0 : vec[i-1].first;
		ans=min(ans,mx2+mx2+mx+min(dis((p+mx)%n,0),dis(0,(p+mx)%n)));
		ans=min(ans,mx+mx+mx2+min(dis((p-mx2+n)%n,0),dis(0,(p-mx2+n)%n)));
	} return ans+vec.size();
}
int main() {
	scanf("%s%s",a,b);
	n=strlen(a);
	int s1=0, s2=0;
	for(int i=0;i<n;i++)
		s1+=a[i]-'0', s2+=b[i]-'0';
	if(s1 && !s2) {return puts("-1"),0;}
	
	if(b[0]=='1') pre[0]=0;
	else {
		for(int i=n-1;~i;i--)
			if(b[i]=='1') {pre[0]=i; break;}
	}
	for(int i=1;i<n;i++)
		pre[i]=(b[i]=='1' ? i : pre[i-1]);
	if(b[n-1]=='1') suf[n-1]=n-1;
	else {
		for(int i=0;i<n;i++) 
			if(b[i]=='1') {suf[n-1]=i; break;} 
	}
	for(int i=n-2;~i;i--)
		suf[i]=(b[i]=='1' ? i : suf[i+1]);
	int ans=1e9;
	for(int i=0;i<n;i++)
		ans=min(ans,solve(i));
		
	reverse(a,a+n); reverse(b,b+n);
	if(b[0]=='1') pre[0]=0;
	else {
		for(int i=n-1;~i;i--)
			if(b[i]=='1') {pre[0]=i; break;}
	}
	for(int i=1;i<n;i++)
		pre[i]=(b[i]=='1' ? i : pre[i-1]);
	if(b[n-1]=='1') suf[n-1]=n-1;
	else {
		for(int i=0;i<n;i++) 
			if(b[i]=='1') {suf[n-1]=i; break;} 
	}
	for(int i=n-2;~i;i--)
		suf[i]=(b[i]=='1' ? i : suf[i+1]);
	for(int i=0;i<n;i++)
		ans=min(ans,solve(i));
	
	cout<<ans<<'\n';
}

Shuffle and Swap

发现合法的就是那些不相等的位置中间插入一些相等的位置。

把这个指数生成函数搞出来做个FFT快速幂即可,时间复杂度 O ( n log ⁡ 2 n ) O(n \log^2 n) O(nlog2n)

#include <bits/stdc++.h>
using namespace std;
 
const int N=1e6+50, mod=998244353;
inline int add(int x,int y) {return (x+y>=mod) ? (x+y-mod) : (x+y);}
inline int dec(int x,int y) {return (x-y<0) ? (x-y+mod) : (x-y);}
inline int mul(int x,int y) {return (long long)x*y%mod;}
inline int power(int a,int b,int rs=1) {for(;b;b>>=1,a=mul(a,a)) if(b&1) rs=mul(rs,a); return rs;}
inline int sgn(int x) {return (x&1) ? (mod-1) : 1;}
 
namespace FFT {
	const int G=3;
	int A[N],B[N],w[N],pos[N],k;
	inline void init(int n) {
		for(k=1;k<=n;k<<=1);
		memset(A,0,sizeof(int)*k);
		memset(B,0,sizeof(int)*k);
		for(int i=1;i<k;i++) pos[i]=(i&1) ? ((pos[i>>1]>>1)^(k>>1)) : (pos[i>>1]>>1);
	}
	inline void dft(int *a) {
		for(int i=1;i<k;i++)
			if(pos[i]>i) swap(a[pos[i]],a[i]);
		for(int bl=1;bl<k;bl<<=1) {
			int tl=bl<<1, wn=power(G,(mod-1)/tl);
			w[0]=1; for(int i=1;i<bl;i++) w[i]=mul(w[i-1],wn);
			for(int bg=0;bg<k;bg+=tl)
				for(int j=0;j<bl;j++) {
					int &t1=a[bg+j], &t2=a[bg+j+bl], t=mul(t2,w[j]);
					t2=dec(t1,t); t1=add(t1,t);
				}
		}
	}
	inline void func() {
		dft(A); dft(B);
		for(int i=0;i<k;i++) B[i]=mul(B[i],A[i]);
		dft(B); const int inv=power(k,mod-2);
		for(int i=0;i<k;i++) B[i]=mul(B[i],inv);
		reverse(B+1,B+k); 
	}
}
struct combin {
	int fac[N],ifac[N];
	combin() {
		fac[0]=1;
		for(int i=1;i<N;i++) fac[i]=mul(fac[i-1],i);
		ifac[0]=ifac[1]=1;
		for(int i=2;i<N;i++) ifac[i]=mul(mod-mod/i,ifac[mod%i]);
		for(int i=2;i<N;i++) ifac[i]=mul(ifac[i-1],ifac[i]); 
	}
	inline int C(int a,int b) {return mul(fac[a],mul(ifac[b],ifac[a-b]));}
} C;
 
struct poly {
	vector <int> a;
	poly(int d=0,int t=0) {a.resize(d+1); a[d]=t;}
	inline int& operator [](const int &i) {return a[i];}
	inline const int& operator [](const int &i) const {return a[i];}
	inline int deg() const {return a.size()-1;}
	inline poly extend(int k) {poly c=*this; c.a.resize(k); return c;}
	friend inline poly operator *(const poly &a,const poly &b) {
		poly c(a.deg()+b.deg()); FFT::init(c.deg());
		for(int i=0;i<=a.deg();i++) FFT::A[i]=a[i];
		for(int i=0;i<=b.deg();i++) FFT::B[i]=b[i];
		FFT::func();
		for(int i=0;i<=c.deg();i++) c[i]=FFT::B[i];
		return c;
	}
	inline void pt() {
		for(int i=0;i<=deg();i++) cerr<<a[i]<<' '; cerr<<'\n';
	}
};
 
int n,m,l;
char a[N],b[N];
int main() {
	scanf("%s%s",a,b);
	l=strlen(a);
	for(int i=0;i<l;i++)
		if(a[i]=='1' &&a[i]==b[i]) ++n;
		else if(a[i]=='1') ++m;
	poly g(n+1,0); 
	for(int i=1;i<=n+1;i++) g[i]=C.ifac[i];
	poly f(n+m,0); f[0]=1;
	for(int b=m;b;b>>=1,g=(g*g).extend(n+m+1))
		if(b&1) f=(f*g).extend(n+m+1);
	int ans=0;
	for(int i=m;i<=n+m;i++) {
		int res=mul(mul(f[i],C.fac[i]),C.C(n+m,i));
		res=mul(res,mul(C.fac[n+m-i],C.fac[n+m-i]));
		res=mul(res,C.C(n,i-m));
		res=mul(res,C.fac[i-m]);
		ans=add(ans,res);
	}
	cout<<mul(ans,C.fac[m]);
}

Yes or No

考虑一个方案,发现会被计算 max ⁡ { n , m } + 对 角 线 经 过 次 数 2 \max\{n,m\}+\frac{对角线经过次数}{2} max{n,m}+2线次,那我们对对角线上每个点算一算会被经过多少次就行了。

#include <bits/stdc++.h>
using namespace std;

const int N=1e6+50, mod=998244353;
inline int add(int x,int y) {return (x+y>=mod) ? (x+y-mod) : (x+y);}
inline int dec(int x,int y) {return (x-y<0) ? (x-y+mod) : (x-y);}
inline int mul(int x,int y) {return (long long)x*y%mod;}
inline int power(int a,int b,int rs=1) {for(;b;b>>=1,a=mul(a,a)) if(b&1) rs=mul(rs,a); return rs;}
inline int inv(int a) {return power(a,mod-2);}

struct combin {
	int fac[N],ifac[N];
	combin() {
		fac[0]=1;
		for(int i=1;i<N;i++) fac[i]=mul(fac[i-1],i);
		ifac[0]=ifac[1]=1;
		for(int i=2;i<N;i++) ifac[i]=mul(mod-mod/i,ifac[mod%i]);
		for(int i=2;i<N;i++) ifac[i]=mul(ifac[i-1],ifac[i]);
	}
	inline int C(int a,int b) {return mul(fac[a],mul(ifac[b],ifac[a-b]));}
} C;
#define x1 dqoaspjdioq
#define x2 dnasuihquih
#define y1 adsjiohejqu
#define y2 dnasuigqyuw
inline int path(int x1,int y1,int x2,int y2) {
	return C.C(x2-x1+y2-y1,x2-x1);
}
int n,m;
int main() {
	cin>>n>>m; if(n>m) swap(n,m);
	int ans=0;
	for(int i=1;i<=n;i++)
		ans=add(ans,mul(path(0,0,i,i),path(i,i,n,m)));
	ans=mul(ans,inv(2));
	ans=mul(ans,inv(path(0,0,n,m)));
	cout<<add(m,ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值