赛事得分:$100 + 0 + 50 + 30$ 鉴定为史。

T1 Palindrome Basis

题面

原题面 CF1673C

题意

问一个数拆成回文数,有多少种拆分方案。

思路

完全背包。想了一个小时差点没想出来。然后局势就很明朗了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// Contest code
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<algorithm>
//#define int long long
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const ll inf=1e18;
const int N=4e4+10;
inline int read(){
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline ll lread(){
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
int dp[N],n;
vector <int> v;
const int P=1e9+7;
void init(){
dp[0]=1;
for(int i=1;i<=9;i++) v.push_back(i);
for(int i=1;i<=9;i++) v.push_back(i*11);
for(int i=1;i<=9;i++) for(int j=0;j<=9;j++) v.push_back(101*i+10*j);
for(int i=1;i<=9;i++) for(int j=0;j<=9;j++) v.push_back(1001*i+110*j);
for(int i=1;i<=9;i++) for(int j=0;j<=9;j++) for(int k=0;k<=9;k++) v.push_back(10001*i+1010*j+100*k);
// for(int i=1;i<=4e4;i++){
// for(auto j:v){
// if(i-j>=0) dp[i]+=dp[i-j];
// else break;
// }
// }
//for(auto i:v) cout<<i<<endl;
for(auto i:v){
for(int j=i;j<=4e4;j++){
dp[j]=(dp[j]+dp[j-i])%P;
}
}
}
signed main(){
init();
int _=read();while(_--) {int a=read();cout<<dp[a]<<endl;}
return 0;
}

T2 Armchairs

题面

原题面 CF1525D

题意

$n$ 把椅子,其中不多于一半有人占着。由 $i$ 换座到 $j$ 代价为 $abs (i-j)$。问所有人都换座到初始没被占的位置代价最小多少。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Contest code
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<algorithm>
//#define int long long
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const ll inf=1e18;
const int N=5e3+10;
inline int read(){
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline ll lread(){
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,val[N],dp[N][N];
int pla[N],plb[N];
int ta,tb;

signed main(){
n=read();for(int i=1;i<=n;i++) val[i]=read();
for(int i=1;i<=n;i++){
if(val[i]) pla[++ta]=i;
else plb[++tb]=i;
}
memset(dp,0x3f,sizeof(dp));
for(int i=0;i<=tb;i++) dp[0][i]=0;
for(int i=1;i<=ta;i++) for(int j=1;j<=tb;j++){
dp[i][j]=min(dp[i-1][j-1]+abs(pla[i]-plb[j]),dp[i][j-1]);
}
cout<<dp[ta][tb]<<endl;
return 0;
}

T3 Choosing Capital for Treeland

题面

原题面 CF219D

题意

给一棵树,边有方向,一个节点作为根节点的权值为指向根节点的边数。求所有节点作为根节点的权值。

思路

将以 $u$ 作为根节点的反转答案即为 $dp[u]$ ,对于每条边 $(u,v)$ ,可以通过边的方向确定 $dp[v]$ ($|dp[u]-dp[v]|=1$)。

懒不证明。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// Contest code
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<algorithm>
//#define int long long
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const ll inf=1e18;
const int N=2e5+10;
inline int read(){
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline ll lread(){
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n;
vector <pii> G[N];
int vl[N];
int minn=1e9;
vector <int> ve[N];
int dfs1(int u,int fa){
int res=0;
for(auto k:G[u]){
int v=k.first,w=k.second;
if(v==fa) continue;
res+=dfs1(v,u)+w;
}
return res;
}
void dfs2(int u,int fa){
for(auto k:G[u]){
int v=k.first,w=k.second;
if(v==fa) continue;
if(w) vl[v]=vl[u]-1;
else vl[v]=vl[u]+1;
dfs2(v,u);
}
minn=min(minn,vl[u]);
ve[vl[u]].push_back(u);
}
signed main(){
n=read();
for(int i=1;i<n;i++){
int u=read(),v=read();
G[u].push_back({v,0});//正
G[v].push_back({u,1});//反
}
vl[1]=dfs1(1,1);
//cout<<vl[1]<<endl;
dfs2(1,1);
// for(int i=1;i<=n;i++) cout<<vl[i]<<" ";
// cout<<endl;
cout<<minn<<endl;
sort(ve[minn].begin(),ve[minn].end());
for(auto i:ve[minn]) cout<<i<<" ";cout<<endl;
return 0;
}

T4 Kaavi and Magic Spell

题面

原题面 CF1336C

题意

给定字符串 $T$ 和 $S$ 。有个空字符 $A$ ,每次可以把 $S$ 的首字母插入 $A$ 的开头或者结尾后删除,最多操作|S|次。求有多少种方案使得 $T$ 是 $A$ 的前缀。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Contest code
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<algorithm>
//#define int long long
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const ll inf=1e18;
const int N=5e3+10;
inline int read(){
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline ll lread(){
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int P=998244353;
int dp[N][N];
signed main(){
string t,s; cin>>s>>t;
int n,m; n=s.length(),m=t.length();
s="?"+s,t="?"+t;
for(int i=1;i<=n;i++) dp[i][i]=(i>m||s[1]==t[i])*2;
for(int len=2;len<=n;len++){
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
if(l>m||s[len]==t[l]) dp[l][r]+=dp[l+1][r],dp[l][r]%=P;
if(r>m||s[len]==t[r]) dp[l][r]+=dp[l][r-1],dp[l][r]%=P;
}
}
int ans=0;
for(int i=m;i<=n;i++) ans+=dp[1][i],ans%=P;
cout<<ans<<endl;
return 0;
}