Codeforces Round 635 Div.2

传送门

都高一了才第一次打 c f cf cf 好像太菜了点。。。

C

d e p − s i z e dep-size depsize 排序输出即可

D

枚举中间值,双指针往中间值靠

E

太菜了打的时候只会直接 d p dp dp O ( n m 2 ) O(nm^2) O(nm2),大概是 d p i , l , r dp_{i,l,r} dpi,l,r 表示第 i i i 位,匹配了 [ l , r ] [l,r] [l,r] 的方案数
这个瓶颈在于每次状态可能从 [ l , r ] [l,r] [l,r] 变成 0 0 0,也就是说之前选的都不算在前缀,现在重新开始匹配
考虑优化这个过程,我们在 T T T 后面加上通配符,每次强制匹配,这样每层的状态就是 O ( n ) O(n) O(n)

#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
typedef long long ll;
cs int N = 3e3 + 50;
cs int Mod = 998244353;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
void Add(int &a, int b){ a = add(a,b); }
void Dec(int &a, int b){ a = dec(a,b); }
void Mul(int &a, int b){ a = mul(a,b); }
char S[N], T[N]; int n, m;
int dp[N][N];
int main(){
	#ifdef FSYolanda
	freopen("1.in","r",stdin);
	#endif
	scanf("%s%s",S+1,T+1); n=strlen(S+1); m=strlen(T+1);
	for(int i=1; i<=m; i++) if(S[1]==T[i]) dp[i][i]=2; 
	for(int i=m+1; i<=n; i++) dp[i][i]=2;
	for(int i=2; i<=n; i++)
	for(int l=1; l<=n; l++){
		int r=l+i-1; if(r>n) break; 
		if(r>m||S[i]==T[r]) Add(dp[l][r],dp[l][r-1]);
		if(l>m||S[i]==T[l]) Add(dp[l][r],dp[l+1][r]);
	} int as = 0;
	for(int i=m; i<=n; i++) Add(as,dp[1][i]);
	cout << as;
	return 0;
}

F

交互题:初始集合是值域 [ 1 , n ] [1,n] [1,n],每个数出现次数 [ 0 , n ] [0,n] [0,n] 的集合,你可以知道当前集合中形如 { i , i , i } , { i − 1 , i , i + 1 } \{i,i,i\},\{i-1,i,i+1\} {i,i,i},{i1,i,i+1} 的子集个数,每次可以插入一个数,要猜出初始集合,插入次数 ≤ n \le n n

有插 2 n 2n 2n 次直接得到答案的,每 3 个分组可以插 4 / 3 n 4/3n 4/3n 次得到答案
每个插一次可以 O ( 2 n ) O(2^n) O(2n) 得到答案,那么我们将上述方法结合一下,就有如下巧妙的构造

考虑插 2 次可以确定 n n n,那么我们先插 n n n,知道 a n − 1 ∗ a n − 2 a_{n-1}*a_{n-2} an1an2,然后插 n − 1 n-1 n1,再插 n n n 得到 ( a n − 1 + 1 ) ∗ ( a n − 2 ) (a_{n-1}+1)*(a_{n-2}) (an1+1)(an2),这样就可以得到 a n − 2 a_{n-2} an2,进一步得到 a n − 1 a_{n-1} an1

那么最终的策略可以为:从 2 2 2 插到 n − 2 n-2 n2,插 n n n,插 n − 1 n-1 n1,插 n n n,然后顺推到 1 1 1
最巧妙的地方就是将 n n n 分到两次加,可以多获得一个信息

#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 105;
typedef long long ll;
ll A, B, dA, dB;
int n; ll a[N], b[N];
void work(int u){ 
	cout<<"+ "<<u<<endl<<endl; 
	dA=A; dB=B;
	scanf("%lld%lld",&A,&B); dA=A-dA; dB=B-dB;
}
int get(ll x){
	if(x==0) return 1;
	for(int i=2; i<=n+2; i++) if((i*(i-1)>>1)==x) return i;
}
int main(){
	#ifdef FSYolanda
	freopen("1.in","r",stdin);
	#endif
	scanf("%d",&n);
	scanf("%lld%lld",&A,&B);
	memset(a,-1,sizeof(a));
	for(int i=2; i<=n-2; i++){
		work(i); b[i]=dB;
	} work(n); ll dt=dB; 
	work(n-1); b[n-1]=dB;
	work(n); a[n]=get(dA)-1; 
	a[n-2]=dB-dt-1;
	a[n-1]=dt/(a[n-2]+1); 
	b[n-1]-=(a[n-2]+1)*(a[n]+1);
	for(int i=n-3; i>=1; i--){
		a[i]=b[i+2]/(a[i+1]+1)-(i>1);
		b[i+1]-=a[i+2]*a[i+3];
		b[i+1]-=a[i+2]*(a[i]+1);
	}
	cout<<"! ";
	for(int i=1; i<=n; i++) cout<<a[i]<<" ";
	cout<<endl<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值