比赛链接
A,B,F,I,K。前面过得比较顺,B读懂题就做了;K很快写了DP,也注意了打完最后一颗子弹后面就不能要了,但一直WA;最后才发现还要注意在哪里结束最后一颗子弹(不一定是打过的最后一列),加了一维过了。

A

分析:

签到题。偶数奇数分别算即可。

代码如下
#include<iostream>
using namespace std;
int T,n;
int even(int l,int r)
{
    int len=r-l+1,ret=len/2;
    if((len%2)&&!(l%2))ret++;
    return ret;
}
int odd(int l,int r)
{
    int len=r-l+1,ret=len/2;
    if((len%2)&&(l%2))ret++;
    return ret;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        int ans=n-((n+2)/2)+1+odd((n+2)/3,n);
        printf("%d\n",ans);
    }
    return 0;
}

B

分析:

由题得到一个循环节长度为lcm的字符串。要找最短的包含所有字符的子串,用双指针即可。注意找的范围应该是循环节的两倍长度,因为可以在两节之间取答案。

代码如下
#include<iostream>
#include<cstring>
using namespace std;
int const N=105,M=6e6+5;
int T,n,a[M],l[N],L,p[N],cnt[30];
char s[N][15];
bool vis[30];
int gcd(int x,int y){return (!y)?x:gcd(y,x%y);}
int lcm(int x,int y)
{
    int g=gcd(x,y);
    return x/g*y;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n); L=1;
        memset(cnt,0,sizeof cnt); memset(vis,0,sizeof vis);
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s[i]+1);
            l[i]=strlen(s[i]+1); L=lcm(L,l[i]); p[i]=1;
        }
        // printf("lcm=%d\n",L);
        L=L*2*n; int nw=1,all=0;
        for(int i=1;i<=L;i++)
        {
            a[i]=s[nw][p[nw]]-'a'; 
            p[nw]++; if(p[nw]>l[nw])p[nw]=1;
            nw++; if(nw>n)nw=1;
            if(!vis[a[i]])all++,vis[a[i]]=1;
        }
        // for(int i=1;i<=L;i++)printf("%c",a[i]+'a'); puts("");
        int pl=1,pr=1,num=1; cnt[a[1]]++;
        while(num<all)
        {
            pr++;
            if(!cnt[a[pr]])num++;
            cnt[a[pr]]++;
        }
        int ans=pr;
        for(;pr<=L;pr++,cnt[a[pr]]++,num+=(cnt[a[pr]]==1))
        {
            while(pl<=pr&&num==all)
            {
                cnt[a[pl]]--;
                if(!cnt[a[pl]])num--;
                pl++;
            }
            ans=min(ans,pr-pl+2);
            // printf("pr=%d pl=%d ans=%d\n",pr,pl,ans);
        }
        printf("%d\n",ans);
    }
    return 0;
}

F

分析:

看到许多平方加加减减,自然会想到用几个相近的减一减看看。
于是就能发现四个连续的数可以构造出一个 $ 4 $ : $ i^2 - (i-1)^2 = 2i-1, (i-2)^2 - (i-3)^2 = 2i-5 $
然后用前四个数可以分别构造出 $ 1,2,3,4 $ ,所以前面搭配后面的一堆 $ 4 $ 就可以组成 $ n $ 了。
输出量很大,必须用cout才不会TLE。在这里cout竟然比printf快,长见识了……

代码如下
#include<iostream>
using namespace std;
int const N=1e6+5;
int T,n;
char out[N];
void prt(int l,int r)
{
    for(int i=l;i<=r;i+=4)//printf("1001");
        // putchar('1'),putchar('0'),putchar('0'),putchar('1');
        cout<<"1001";
    puts("");
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        if((n%4)==0)printf("%d\n",n),prt(1,n);
        else if((n%4)==1)printf("%d\n1",n),prt(2,n);
        else if((n%4)==2)printf("%d\n0001",n+2),prt(5,n+2);
        else if((n%4)==3)printf("%d\n01",n-1),prt(3,n-1);
    }
    return 0;
}

I

分析:

求L和R、U和D数量相同的子串个数。
我一开始还是没想清楚……其实可以看作是 $ L-R=0, U-D=0 $ 的子串;也就是左右端点 $ L-R $ 的前缀和、 $ U-D $ 的前缀和都对应相等的子串。所以计算前缀和,开一个map存当前两个前缀和的状态出现过的次数,就可以统计答案了。
比赛时G没有开long long,WA了一次。我看了一会才看出来。

代码如下
#include<iostream>
#include<cstdio>
#include<map>
#define ll long long
#define mkp make_pair
#define fst first
#define scd second
using namespace std;
int const N=1e5+5;
int T,n;
char st[N];
map<pair<int,int>,int>mp;
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%s",&n,st+1); int lr=0,ud=0;
        mp.clear(); ll ans=0; mp[mkp(0,0)]++;
        for(int i=1;i<=n;i++)
        {
            char x=st[i];
            if(x=='U')ud++; else if(x=='D')ud--;
            if(x=='L')lr++; else if(x=='R')lr--;
            ans+=mp[mkp(ud,lr)]; mp[mkp(ud,lr)]++;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

K

分析:

很直观的DP。唯一要注意的是打完最后一发子弹以后,后面那些虽然不损失子弹但是要透支一颗子弹才能获取的分数也不能获取了。所以要注意一下在哪列打完最后一发子弹,也就是DP增加一维 $ 0/1 $ 表示是否已选择了打最后一发子弹的列。

代码如下
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int const N=205,inf=2e9;
int T,n,m,K,g[N][N],g2[N][N],f[N][N][2],d[N][N],mx[N];
char c[N][N];
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&K);
        memset(g,0,sizeof g); memset(g2,0,sizeof g2);
        for(int i=0;i<=m;i++)
            for(int j=0;j<=K;j++)f[i][j][0]=f[i][j][1]=-inf;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d %c",&d[i][j],&c[i][j]);
        for(int j=1,w=0;j<=m;j++,w=0)
            for(int i=n;i;i--)
            {
                if(c[i][j]=='N')w++,g[j][w]=g[j][w-1]+d[i][j],g2[j][w]+=d[i][j],g2[j][w]+=g2[j][w-1];
                else g[j][w]+=d[i][j],g2[j][w+1]+=d[i][j];
                if(i==1)mx[j]=w;
            }
        // for(int i=1;i<=m;i++)
        //     for(int j=0;j<=K;j++)printf("g[%d][%d]=%d g2[%d][%d]=%d\n",i,j,g[i][j],i,j,g2[i][j]);
        f[0][K][0]=0;
        for(int i=1;i<=m;i++)
        {
            for(int j=0;j<=K;j++)
            {
                for(int k=0;j+k<=K&&k<=mx[i];k++)
                {
                    f[i][j][0]=max(f[i][j][0],f[i-1][j+k][0]+g[i][k]);
                    f[i][j][1]=max(f[i][j][1],f[i-1][j+k][1]+g[i][k]);
                    if(k)f[i][j][1]=max(f[i][j][1],f[i-1][j+k][0]+g2[i][k]);//k!=0!!
                    // printf("f[%d][%d][0]=%d f[%d][%d][1]=%d k=%d\n",i,j,f[i][j][0],i,j,f[i][j][1],k);
                }
            }
        }
        int ans=0;
        for(int i=1;i<=m;i++)
            for(int j=0;j<=K;j++)
                ans=max(ans,f[i][j][1]);
        // printf("%d\n",f[m][0][1]);
        printf("%d\n",ans);
    }
    return 0;
}