A
由于整个串长度为 2 n − 1 2n-1 2n−1,所以每个长度为 n n n 的串都肯定会覆盖到第 n n n 个位置,所以直接令答案字符串全部为为第 n n n 个字符即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 100010
int T,n;
char s[maxn];
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%d",&n);
scanf("%s",s+1);
for(int i=1;i<=n;i++)printf("%c",s[n]);printf("\n");
}
}
B
枚举第一个人拿多少把剑,那么剩下的拿法就确定了:用斧头塞满第一个人的背包,第二个人尽可能拿重量小的,如果背包还有空就放满另一种武器。
代码如下:
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 100010
int T,n,m,ca,cb,va,vb;
char s[maxn];
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%d %d %d %d %d %d",&n,&m,&ca,&cb,&va,&vb);
int ans=0;
if(n<m)swap(n,m);
if(va>vb)swap(ca,cb),swap(va,vb);
for(int i=0;i<=ca;i++){
if(1ll*i*va>1ll*n)break;
int tot=i,c,d,e;
c=min((n-i*va)/vb,cb);tot+=c;
d=min(m/va,ca-i);tot+=d;
e=min((m-d*va)/vb,cb-c);tot+=e;
ans=max(ans,tot);
}
printf("%d\n",ans);
}
}
C
容易发现,如果 s i = 0 s_i=0 si=0,那么 w i − x w_{i-x} wi−x 和 w i + x w_{i+x} wi+x 一定是 0 0 0,然后让 w w w 剩下的位全部放 1 1 1,再去检查一下 s i = 1 s_i=1 si=1 的位,看看 w i + x w_{i+x} wi+x 和 w i − x w_{i-x} wi−x 有没有 1 1 1 即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 100010
int T,n,c;
char s[maxn],ans[maxn];
bool tf;
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%s",s+1);n=strlen(s+1);
scanf("%d",&c);tf=false;
for(int i=1;i<=n;i++)ans[i]='1';
for(int i=1;i<=n;i++)if(s[i]=='0'){
if(i-c>=1)ans[i-c]='0';
if(i+c<=n)ans[i+c]='0';
}
for(int i=1;i<=n;i++)if(s[i]=='1'){
int tot=0;
if(i-c>=1)tot|=(ans[i-c]=='1');
if(i+c<=n)tot|=(ans[i+c]=='1');
if(!tot)tf=true;
}
if(tf)printf("-1\n");
else{
for(int i=1;i<=n;i++)printf("%c",ans[i]);
printf("\n");
}
}
}
D
枚举 i , k i,k i,k 或枚举 j , l j,l j,l 统计一下就好,我是枚举的 i , k i,k i,k,但是枚举 j , l j,l j,l 会更简单。
代码如下:
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 3010
#define ll long long
int T,n,a[maxn],A[maxn],B[maxn];
ll ans,now;
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%d",&n);ans=0;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
now=0;
memset(A,0,sizeof(A));//A记录(i,j)中数字的出现次数
memset(B,0,sizeof(B));//B记录(j,n]中数字的出现次数
for(int j=n;j>=i+1;j--)B[a[j]]++;
for(int j=i+1;j<=n;j++){
B[a[j]]--;now-=A[a[j]];
if(a[i]==a[j])ans+=now;
A[a[j]]++;now+=B[a[j]];
}
}
printf("%lld\n",ans);
}
}
E
对于当前序列,只有两种操作可以选择:找到最小的数 x x x,操作 x x x 次使全部数减 x x x,此时序列被 0 0 0 分成了两部分,递归进去处理即可;操作 序列长度 次使每个数都变成 0 0 0。
套个笛卡尔树可以做到 O ( n ) O(n) O(n),但是 n n n 只有 5000 5000 5000 直接暴力即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 100010
#define ll long long
int T,n,a[maxn];
int solve(int l,int r){
if(l>r)return 0;
if(l==r)return a[l]>0;
int mi=1e9,pos,p=0;
for(int i=l;i<=r;i++){
if(a[i]<mi)mi=a[i],pos=i;
p+=(a[i]>0);
}
if(mi>=p)return p;
for(int i=l;i<=r;i++)a[i]-=mi;
return min(solve(l,pos-1)+solve(pos+1,r)+mi,p);
}
int main()
{
T=1;while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
printf("%d\n",solve(1,n));
}
}
上面是赛时写的,后来膜别人代码得到了更短的写法,但思路是一样的:
#include <cstdio>
#include <algorithm>
using namespace std;
int n,a[5010];
int solve(int l,int r,int mi){
if(l>r)return 0;
int c=min_element(a+l,a+r+1)-a;
return min(solve(l,c-1,a[c])+solve(c+1,r,a[c])+a[c]-mi,r-l+1);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
printf("%d",solve(1,n,0));
}
F
由于 x x x 很小,所以可以爆搜出所有 x -prime x\text{-prime} x-prime 字符串,然后造一个 AC \text{AC} AC 自动机跑 dp \text{dp} dp 即可,转移的时候枚举每个节点,然后考虑这个字符删不删即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100010
int n,k;
char s[maxn];
struct node{
int ne[10],fail,end;};
node st[maxn];int t=0;
void insert(char *ss,int len){
int now=0;
for(int i=0;i<len;i++){
if(!st[now].ne[ss[i]-'0'])st[now].ne[ss[i]-'0']=++t;
now=st[now].ne[ss[i]-'0'];
}
st[now].end=1;
}
int q[maxn],S=1,T=0;
void make_fail(){
st[0].fail=-1;
for(int i=0;i<10;i++)if(st[0].ne[i])q[++T]=st[0].ne[i];
while(S<=T){
int x=q[S++];
for(int i=0;i<10;i++)if(st[x].ne[i]){
int j=st[x].fail;
while(j!=-1&&!st[j].ne[i])j=st[j].fail;
if(j!=-1)st[st[x].ne[i]].fail=st[j].ne[i];
q[++T]=st[x].ne[i];
}
}
}
char d[maxn];int dd=0;
bool check(){
for(int i=0;i<dd;i++){
int sum=0;
for(int j=i;j<dd;j++){
sum+=d[j]-'0';
if(sum>=k)break;
if(k%sum==0)return false;
}
}
return true;
}
void dfs(int x){
if(!x){
if(check())insert(d,dd);
return;
}
for(int i=1;i<=min(x,9);i++){
d[dd++]=i+'0';
dfs(x-i);
dd--;
}
}
int f[maxn],g[maxn];
void chkmin(int &x,int y){
if(y<x)x=y;}
int main()
{
scanf("%s",s);n=strlen(s);
scanf("%d",&k);dfs(k);make_fail();
memset(f,63,sizeof(f));f[0]=0;
for(int i=0;i<n;i++){
for(int j=0;j<=t;j++)g[j]=n;
for(int j=0;j<=t;j++){
chkmin(g[j],f[j]+1);//删
int now=j;
while(now&&!st[now].ne[s[i]-'0'])now=st[now].fail;
if(st[now].ne[s[i]-'0'])now=st[now].ne[s[i]-'0'];
if(!st[now].end)chkmin(g[now],f[j]);//不删
}
swap(f,g);
}
int ans=n;
for(int i=0;i<=t;i++)chkmin(ans,f[i]);
printf("%d",ans);
}
G
考虑没有相互讨厌
这个限制时怎么做:令 c i c_i ci 表示有多少个人的区间 [ l i , r i ] [l_i,r_i] [li,ri] 包含 i i i,答案就是 ∑ i = 1 n C c i i \sum_{i=1}^n C_{c_i}^i ∑i=1nCcii。
有了相互讨厌的限制也不难做,考虑容斥即可,假设此时至少包含有 k k k 对相互讨厌的人,这 k k k 对中不同的人有 d d d 个,那么贡献就是 ( − 1 ) k ∑ i = 1 n C c i − d i − d (-1)^k\sum_{i=1}^n C_{c_i-d}^{i-d} (−1)k∑i=1nCci−di−d。
由于 d ≤ 40 d\leq 40 d≤40,所以可以 O ( 40 n ) O(40n) O(40n) 预处理,容斥时 O ( 1 ) O(1) O(1) 求贡献。(赛时脑子抽了,没发现范围很小可以预处理,当时直接暴力容斥居然还过了 73 73 73 个点qwq)
还有一些细节就看代码吧:
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 300010
#define mod 998244353
int T,n,m,l[maxn],r[maxn],a[maxn],b[maxn];
int ksm(int x,int y){
int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
int fac[maxn],inv_fac[maxn];
void work(){
fac[0]=inv_fac[0]=1;
for(int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%mod;
inv_fac[n]=ksm(fac[n],mod-2);
for(int i=n-1;i>=1;i--)inv_fac[i]=1ll*inv_fac[i+1]*(i+1)%mod;
}
int C(int x,int y){
return 1ll*fac[x]*inv_fac[y]%mod*inv_fac[x-y]%mod;}
int s[maxn],sum[maxn][41],ans;
void add(int &x,int y){
x=(x+y>=mod?x+y-mod:x+y);}
void dec(int &x,int y){
x=(x-y<0?x-y+mod:x-y);}
bool v[maxn];
int main()
{
scanf("%d %d",&n,&m);work();
for(int i=1;i<=n;i++)scanf("%d %d",&l[i],&r[i]),s[l[i]]++,s[r[i]+1]--;
for(int i=1;i<=n;i++)s[i]+=s[i-1];
for(int k=0;k<=40;k++){
for(int i=1;i<=n;i++){
sum[i][k]=sum[i-1][k];
if(i>=k&&s[i]>=i)add(sum[i][k],C(s[i]-k,i-k));
}
}
ans=sum[n][0];
for(int i=1;i<=m;i++)scanf("%d %d",&a[i],&b[i]);
for(int i=1;i<(1<<m);i++){
int L=0,R=1e9,p=1,tot=0;
for(int j=0;j<m;j++)if(i>>j&1){
L=max(L,max(l[a[j+1]],l[b[j+1]]));
R=min(R,min(r[a[j+1]],r[b[j+1]]));
if(!v[a[j+1]])v[a[j+1]]=true,tot++;
if(!v[b[j+1]])v[b[j+1]]=true,tot++;
p*=-1;
}
L=max(L,tot);if(L<=R){
if(p==1)add(ans,(sum[R][tot]-sum[L-1][tot]+mod)%mod);
else dec(ans,(sum[R][tot]-sum[L-1][tot]+mod)%mod);
}
for(int j=0;j<m;j++)if(i>>j&1){
v[a[j+1]]=false;
v[b[j+1]]=false;
}
}
printf("%d",ans);
}