2023 CCPC河南省赛题解12/12

https://codeforces.com/gym/104354

 A

注意到a串最大长度也就26,所以可以枚举a串,判断剩下的串是不是回文的。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll read()
{
    ll x;scanf("%lld",&x);return x;
}
char s[100010];
int f[300],n;
int check(int l,int r)
{
    while(l<r)
    {
        if(s[l]!=s[r])
            return 0;
        l++;
        r--;
    }
    return 1;
}
void work()
{
    scanf("%s",s+1);
    n=strlen(s+1);
    for(int i='a';i<='z';i++)
        f[i]=0;
    for(int i=1;i<n;i++)
    {
        if(f[s[i]])
            break;
        f[s[i]]=1;
        if(check(i+1,n))
        {
            printf("HE\n");
            return ;
        }
    }
    printf("NaN\n");
}
int main()
{
    // freopen("1.in","r",stdin);
    for(int t=read();t;t--)
        work();
}
A

B

考虑如果有ai>aj,i<j,那么i和j必须在同一个块里。否则随便划分。

所以贪心地划分成最小的不可再分的几个区间。从l到i,i是右端点当且仅当区间最大值小于等于后面的最小值。

把这些右端点标记一下,满足答案的k必须满足k、2k、3k、4k...都被标记。

预处理后缀最小值,再暴力判断一下合法的k,复杂度nlogn。

int n,a[1000010],m[1000010],f[1000010],ans;
int main()
{
    // freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    m[n+1]=a[n];
    for(int i=n;i>=1;i--)
        m[i]=min(m[i+1],a[i]);
    int maxx=0;
    for(int i=1;i<=n;i++)
    {
        maxx=max(maxx,a[i]);
        if(maxx<=m[i+1]||i==n)
        {
            f[i]=1;
            maxx=0;
        }
    }
    for(int k=1;k<=n;k++)
    {
        ans++;
        for(int i=k;i<=n;i+=k)
            if(f[i]==0)
            {
                ans--;
                break;
            }
    }
    printf("%d",ans);
}
B

C

先跑一个kmp。

因为前100个和中间某一段相同的概率很小,所以kmp一但发现f[i]大于等于100就认为不是随机数即可。

int n,f[1000010];
char s[1000010];
int main()
{
    // freopen("1.in","r",stdin);
    scanf("%s",s);
    n=strlen(s);
    for(int i=1;i<n;i++)
    {
        int j=f[i-1];
        while(j>0&&s[i]!=s[j])
            j=f[j-1];
        if(s[i]==s[j])
            j++;
        f[i]=j;
        if(f[i]>=100)
        {
            printf("No");
            return 0;
        }
    }
    printf("Yes");
}
C

D

首先来点特例:

n=1或者颜色大于边数的话输出1 1。把没有连边的点删了,删了不会影响答案。删了若干个点之后发现某个点没有连边了,那么也需要删掉。可以用dfs来做这个过程。如果剩下没点了也可以输出11。

那么带着非孤立点和有用的边继续出发。

考虑做n轮删边操作:先用当前的边跑并查集。然后对于每条边,如果相连的点的归属情况不同,就可以把这个边删了。

删了这么多边后,就只剩下有用的边,并且所有的点所属并查集情况相同。找一找最大的并查集大小并输出即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll read() {
    ll x;
    scanf("%lld",&x);
    return x;
}
#define M 2000010
#define N 501
struct edge {
    int x,y,v;
    friend bool operator <(edge x,edge y) {
        return x.v<y.v;
    }
} e[M];
int n,m,in[N],sum[N],minf[N],maxf[N];
vector<short>f[N];
int k;
vector<pair<int,int>>ee[N];
int checkin(int x) {
    for(int i=1; i<=k; i++)
        if(f[x][i]==0)
            return 0;
    return 1;
}
void dfs(int x) {
    in[x]=0;
    for(auto y:ee[x]) {
        if(in[y.first]==0)continue;
        f[y.first][y.second]--;
        if(f[y.first][y.second]==0)
            sum[y.first]--;
        if(sum[y.first]==0)
            dfs(y.first);
    }
}
int getfa(int x,int v) {
    return f[x][v]==x?x:f[x][v]=getfa(f[x][v],v);
}
void merge(int v,int x,int y) {
    x=getfa(x,v);
    y=getfa(y,v);
    if(x==y)return ;
    if(x>y)swap(x,y);
    if(v==1)
        sum[y]+=sum[x];
    f[x][v]=y;
}
vector<int>num,t[N];
vector<int>resn;
void sortn() {
    num.clear();
    for(auto i:resn)
        num.push_back(i);
    for(int i=1; i<=k; i++) {
        for(auto x:resn)
            t[x].clear();
        for(auto x:num) {
            t[getfa(x,i)].push_back(x);
        }
        num.clear();
        for(auto i:resn)
            for(auto x:t[i])
                num.push_back(x);
    }
}
int check() {
    for(auto i:resn)
    {
        minf[i]=maxf[i]=getfa(i,1);
        for(int j=1; j<=k; j++)
        {
            
            minf[i]=min(minf[i],(int)f[i][j]);
            maxf[i]=max(maxf[i],(int)f[i][j]);
        }

    }
    for(auto i:resn)
        for(int j=1; j<=k; j++)
            if(f[i][j]!=f[i][1])
                return 0;
    return 1;
}
void work() {
    k=read();
    n=read();
    m=read();
    resn.clear();
    for(int i=1; i<=n; i++)
        ee[i].clear();
    for(int i=1; i<=m; i++) {
        e[i].v=read();
        e[i].x=read();
        e[i].y=read();
        ee[e[i].x].push_back({e[i].y,e[i].v});
        ee[e[i].y].push_back({e[i].x,e[i].v});
    }
    if(n==1||k>m) {
        printf("1\n1\n");
        return ;
    }
    for(int i=1; i<=n; i++)
        f[i].resize(m+1);
    for(int i=1; i<=n; i++) {
        in[i]=1;
        for(int j=1; j<=m; j++)
            f[i][j]=0;
    }
    for(int i=1; i<=m; i++) {
        if(f[e[i].x][e[i].v]==0)
            sum[e[i].x]++;
        if(f[e[i].y][e[i].v]==0)
            sum[e[i].y]++;
        f[e[i].x][e[i].v]++;
        f[e[i].y][e[i].v]++;
    }
    for(int i=1; i<=n; i++) {
        if(in[i]&&checkin(i)==0) {
            dfs(i);
        }
        if(in[i])
            resn.push_back(i);
    }
    if(resn.size()==0)
    {
        printf("1\n1\n");
        return ;
    }
    int t=0;
    for(int i=1; i<=m; i++) {
        if(in[e[i].x]==0||in[e[i].y]==0)
            continue;
        t++;
        e[t]=e[i];
    }
    m=t;
    for(int jj=1;jj<=resn.size();jj++)
    {
        for(auto i:resn) {
            for(int j=1; j<=k; j++) {
                f[i][j]=i;
            }
            sum[i]=1;
        }
        for(int i=1; i<=m; i++) {
            merge(e[i].v,e[i].x,e[i].y);
        }
        sortn();
        if(check()) {
            break;
        }
        int t=0;
        for(int i=1; i<=m; i++) 
        {
            if(minf[e[i].x]==minf[e[i].y]&&maxf[e[i].x]==maxf[e[i].y])
            {
                t++;
                e[t]=e[i];
            }
        }
        m=t;
    }
    int ans=resn[0];
    for(auto i:resn)
        if(sum[getfa(i,1)]>sum[f[ans][1]])
            ans=i;
    printf("%d\n",sum[f[ans][1]]);
    for(auto i:resn)
        if(f[ans][1]==getfa(i,1))
            printf("%d ",i);
    printf("\n");
}
int main() {
    // freopen("1.in","r",stdin);
    // freopen("1.out","w",stdout);
    for(int t=read(); t; t--)
        work();
}
D

 E

dp一下

基本转移方式是从f[i-1][j][k]和f[i][j-1][k]转移过来,如果是1可以+1,否则不能加

高级的转移是如果当前是问号,可以从f[i-1][j][k-1]和f[i][j-1][k-1]转移过来并加一。如果k=0就不能有这个转移。

空间开不下,所以压缩一下。

int n,m,x,f[2][510][1010];
char s[510][510];
void work()
{
    n=read();m=read();x=read();
    for(int i=1;i<=n;i++)
        scanf("%s",s[i]+1);
    for(int i=0;i<=m;i++)
        for(int j=0;j<=x;j++)
            f[0][i][j]=f[1][i][j]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(s[i][j]=='1')
                f[i&1][j][0]=max(f[i&1][j-1][0],f[(i-1)&1][j][0])+1;
            else
                f[i&1][j][0]=max(f[i&1][j-1][0],f[(i-1)&1][j][0]);
            for(int k=1;k<=x;k++)
            {
                if(s[i][j]=='1')
                    f[i&1][j][k]=max(f[i&1][j-1][k],f[(i-1)&1][j][k])+1;
                else
                    f[i&1][j][k]=max(f[i&1][j-1][k],f[(i-1)&1][j][k]);
                if(s[i][j]=='?')
                    f[i&1][j][k]=max(f[i&1][j][k], max(f[i&1][j-1][k-1],f[(i-1)&1][j][k-1])+1);
            }
        }
    }
    int ans=f[n&1][m][0];
    for(int i=1;i<=x;i++)
        ans=max(ans,f[n&1][m][i]);
    printf("%d\n",ans);
}
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("1.out","w",stdout);
    for(int t=read();t;t--)
        work();
}
E

F

sort一下,然后枚举所有长为k的子串。

min(|a[i]-a[j]|)是所有相邻数字的差的最小值,用multiset维护一下。max(|a[i]-a[j]|)显然是子串的左右两端。

ll n,k,a[500010],ans;
multiset<ll>o;
int main()
{
    n=read();k=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    sort(a+1,a+1+n);
    for(int i=1;i<k;i++)
        o.insert(a[i+1]-a[i]);
    ans=*o.begin()*(a[k]-a[1]);
    for(int i=k;i<=n;i++)
    {
        ans=min(ans,*o.begin()*(a[i]-a[i-k+1]));
        o.erase(o.find(a[i-k+2]-a[i-k+1]));
        o.insert(a[i+1]-a[i]);
    }
    printf("%lld",ans);
}
F

 

G

大胆模拟

判断INF我刚开始用了log10,wa了,就换了用1e18除x的方式,稳定不出错。

害怕substr慢就用模拟的方式写了写。本来ans[i]+=substr就很好写的。

以及赛时我只能用pta。如果给我dev的话处理数据本来很快的。

郑轻罪大恶极

char a[][100]={"",
".................................................................................",
".................................................................................",
".0000000.......1.2222222.3333333.4.....4.5555555.6666666.7777777.8888888.9999999.",
".0.....0.......1.......2.......3.4.....4.5.......6.............7.8.....8.9.....9.",
".0.....0.......1.......2.......3.4.....4.5.......6.............7.8.....8.9.....9.",
".0.....0.......1.2222222.3333333.4444444.5555555.6666666.......7.8888888.9999999.",
".0.....0.......1.2.............3.......4.......5.6.....6.......7.8.....8.......9.",
".0.....0.......1.2.............3.......4.......5.6.....6.......7.8.....8.......9.",
".0000000.......1.2222222.3333333.......4.5555555.6666666.......7.8888888.9999999.",
".................................................................................",
};
char b[100][100]={"",
".............................................................",
".00000.....1.22222.33333.4...4.55555.66666.77777.88888.99999.",
".0...0.....1.....2.....3.4...4.5.....6.........7.8...8.9...9.",
".0...0.....1.22222.33333.44444.55555.66666.....7.88888.99999.",
".0...0.....1.2.........3.....4.....5.6...6.....7.8...8.....9.",
".00000.....1.22222.33333.....4.55555.66666.....7.88888.99999.",
".............................................................",
".............................................................",
".............................................................",
".............................................................",
};
char c[100][100]={"",
".................................",
".................................",
".........IIIIIII.N.....N.FFFFFFF.",
"............I....NN....N.F.......",
".=======....I....N.N...N.F.......",
"............I....N..N..N.FFFFFFF.",
".=======....I....N...N.N.F.......",
"............I....N....NN.F.......",
".........IIIIIII.N.....N.F.......",
".................................",
};
char ans[20][1000];
int tot;
void add(ll x,int d)
{
    stack<int>q;
    while(x)
    {
        q.push(x%10);
        x=x/10;
    }
    while(q.size())
    {
        for(int i=1;i<=10;i++)
        {
            if(d==1)
                for(int j=tot,k=q.top()*8;j<tot+8;j++,k++)
                    ans[i][j]=a[i][k];
            else
                for(int j=tot,k=q.top()*6;j<tot+6;j++,k++)
                    ans[i][j]=b[i][k];
        }
        if(d==1)
            tot+=8;
        else
            tot+=6;
        q.pop();
    }
}
void INV(int l,int r)
{
    for(int i=1;i<=10;i++)
        for(int j=tot,tl=l;tl<=r;j++,tl++)
            ans[i][j]=c[i][tl];
    tot=tot+r-l+1;
}
int check(ll x,ll y)
{
    if(x==1)
        return 0;
    ll a=1e18;
    for(int i=1;i<=y;i++)
    {
        if(a<x)
            return 1;
        a=a/x;
    }
    return 0;
}
ll quick(ll x,ll y)
{
    if(x==1)
        return 1;
    ll t=1;
    for(;y;y--)
        t=t*x;
    return t;
}
void work()
{
    ll x,y;
    scanf("%lld^{%lld}",&x,&y);
    tot=0;
    add(x,1);
    add(y,2);
    INV(0,7);
    if(check(x,y))
        INV(8,31);
    else
        add(quick(x,y),1);
    for(int i=1;i<=10;i++)
    {
        ans[i][tot]=0;
        printf("%s.\n",ans[i]);
    }
    printf("\n");
}
int main()
{
    // freopen("1.out","w",stdout);
    for(int t=read();t;t--)
        work();
}
G

H

把玩一下推式子即可。再和记得给下界0取max,给上界n*2取min。

int n,k;
void work()
{
    n=read();k=read();
    printf("%d %d\n",max(0,n-(k-1)/2),min(n*2,n+k/2));
}
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("1.out","w",stdout);
    for(int t=read();t;t--)
        work();
}
H

I

来自官方题解

 考虑假如没有正方形,那么纯色2*2是4*n*n个

那么加上n个矩形,每个矩形的四条边上的2*2正方形都是非纯色的,所以答案减去2*X2-2*X1+2*Y2-2*Y1

如果矩形之间没有交点,那么到这里输出就行了,例如样例二

但是如果有交点,那么每个交点会使得一个2*2多减去了一次,我们要加上不同矩形间的交点数量,例如样例一答案是4*3*3-4-8-8+2=18

 

因为本题的特殊性,求交点可以使用树状数组:

对于每条竖线,记录下面的两个端点放在jia数组里,上面的两个端点放在jian数组里。每条横线放在lr里

因为每行一条线段,所以lr数组是满的。同时每行jia数组或者jian数组有且只有一个

考虑枚举每一行,先减,再询问,再加。用树状数组实现单点修改区间询问。

int c[200010],n,jia[200010][2],jian[200010][2],l[200010],r[200010];
ll ans;
int lowbit(int x)
{
    return x&(-x);
}
int ask(int d)
{
    int sum=0;
    while(d)
    {
        sum=sum+c[d];
        d=d-lowbit(d);
    }
    return sum;
}
void add(int d,int v)
{
    while(d<=n)
    {
        c[d]+=v;
        d=d+lowbit(d);
    }
}
int main()
{
    // freopen("1.in","r",stdin);
    n=read();
    ans=4ll*n*n;
    int X1,X2,Y1,Y2;
    for(int i=1;i<=n;i++)
    {
        X1=read();Y1=read();
        X2=read();Y2=read();
        jia[Y1][0]=X1;
        jia[Y1][1]=X2;
        jian[Y2][0]=X1;
        jian[Y2][1]=X2;
        l[Y1]=X1;
        r[Y1]=X2;
        l[Y2]=X1;
        r[Y2]=X2;
        ans-=X2*2-X1*2+Y2*2-Y1*2;
    }
    n=n*2;
    for(int i=1;i<=n;i++)
    {
        if(jian[i][0])
        {
            add(jian[i][0],-1);
            add(jian[i][1],-1);
        }
        ans+=(ask(r[i])-ask(l[i]-1));
        if(jia[i][0])
        {
            add(jia[i][0],1);
            add(jia[i][1],1);
        }
    }
    cout<<ans;
}
I

J

初中数学。初中数学好并且acos用的熟练即可。不知道为什么赛时没人写(以及我上机最后半小时很急没调出来)

首先点P到线段AB的距离可以用勾股定理求出。斜边为AP,一个直角边为AB/2设为AD,那么P到线段AB的距离即为d=sqrt(ap*ap-ad*ad);

然后开始分类讨论:

如果AB两点在圆内也就是AP<=r,则输出圆的面积

情况二:

 

 

若P到AB距离小于等于r,则如上图所示。四个三角形的面积可以勾股定理:斜边为AP,一个直角边为r,则面积为r*sqrt(ap*ap-r*r)/2,四个三角形面积为r*sqrt(ap*ap-r*r)*2

剩下的两个扇形考虑使用弧度制,圆心角为2acos(-1),∠APC所对圆心角为acos(r/AP),故剩下的两个扇形所对圆心角为2*acos(-1)-4*acos(r/AP),面积为(2*acos(-1)-4*acos(r/AP))*r*r/2=(acos(-1)-acos(r/ap)*2)*r*r

情况三:

 

 两个小三角形面积可以用勾股定理:斜边为AP,一个直角边为r,则面积为r*sqrt(ap*ap-r*r)/2,两个三角形面积为r*sqrt(ap*ap-r*r)

△ABP的面积为ad*d

圆心角∠ACP的度数为acos(r/ap),圆心角∠APD的度数为acos(d/ap)

所以剩下的扇形所对圆心角为2*acos(-1)-2*acos(r/ap)-2*acos(d/ap)

剩下的扇形的面积为(2*acos(-1)-2*acos(r/ap)-2*acos(d/ap))*r*r/2=(acos(-1)-acos(d/ap)-acos(r/ap))*r*r

总面积为ad*d+r*sqrt(ap*ap-r*r)+(acos(-1)-acos(d/ap)-acos(r/ap))*r*r

 

struct node
{
    ll x,y;
    void in()
    {
        x=read();
        y=read();
    }
}p,a,b;
ll r;
double ap,ad,d;
double askd(node x,node y)
{
    return sqrt( (x.x-y.x)*(x.x-y.x)+ (x.y-y.y)*(x.y-y.y));
}
ll askdd(node x,node y)
{
    return (x.x-y.x)*(x.x-y.x)+ (x.y-y.y)*(x.y-y.y);
}
void work()
{
    p.in();
    a.in();
    b.in();
    r=read()*read();
    ap=askd(a,p);
    ad=askd(a,b)/2;
    d=sqrt(ap*ap-ad*ad);
    if(askdd(a,p)<=r*r)
    {
        printf("%.12lf\n",acos(-1)*r*r);
    }
    else if(d<=r)
    {
        printf("%.12lf\n",r*sqrt(ap*ap-r*r)*2+ (acos(-1)-acos(r/ap)*2)*r*r);
    }
    else
    {
        double t=sqrt(ap*ap-r*r);
        printf("%.12lf\n",d*ad+r*t+(acos(-1)-acos(d/ap)-acos(r/ap))*r*r);
    }
}
int main()
{
    // freopen("1.in","r",stdin);
    for(int t=read();t;t--)
        work();
}
J

 K

唉赛时卡了半天。现在想想主要是hs证明了不能只用2和3,然后就误入歧途了。

void work(int n){
    if(n<=4)
        printf("-1\n");
    else if(n==5)
        printf("4 1 3 5 2\n");
    else if(n==6)
        printf("1 3 5 2 4 6\n");
    else if(n==7)
        printf("1 3 5 2 7 4 6\n");
    else if(n==8)
        printf("1 6 3 8 5 2 7 4\n");
    else if(n==11)
        printf("1 3 10 5 2 7 9 11 8 6 4\n");
    else
    {
        for(int i=1;i+2<=n;i+=2)
        {
            printf("%d ",i);
            if(i==5)
                printf("2 ");
            // if(i==n-6&&i+2!=5)
            if(i==n-6)
                printf("%d ",n-1);
        }
        printf("%d ",n);
        for(int i=n-(n&1)-2;i>=4;i-=2)
        {
            printf("%d ",i);
            if(i==n-4)
                printf("%d ",n-1);
        }
        printf("\n");
    }

}
int main()
{
    // freopen("1.in","r",stdin);
    // freopen("1.out","w",stdout);
    // for(int t=read();t;t--)
    work(read());
}

F
K

L

从jiangly代码里抄了一个getPrime才过

 

int test[]={2,3,5,7,11,13,17,23,29,31,37};
unordered_map<int,bool>mp;
ll qpow(ll x,ll y,ll P){
    ll ret=1;
    for (;y;y>>=1,x=x*x%P)
        if (y&1) ret=ret*x%P;
    return ret;
}
bool check(int a,ll P){
    ll r=P-1,g=qpow(a,r,P);
    if (g!=1) return 1;
    while (r%2==0){
        r/=2;
        g=qpow(a,r,P);
        if (g==P-1) return 0;
        else if (g!=1) return 1;
    }
    return 0;
}
bool miller_rabin(int P){
    for (int a:test){
        if (check(a,P)) return 0; 
    }
    return 1;
}
int rnd(){
    int t=rand();
    t^=rand()<<8;
    t^=rand()<<16;
    t^=rand()<<24;
    return t<0?-t:t;
}
int getPrime(){
    ll t1=rnd()%500000000+5e8;
    ll t2=1e9-t1;
    int x=rnd()%t2+t1;
    while (!miller_rabin(x)||mp.find(x)!=mp.end()) 
        x=rand()%t2+t1;
    mp[x]=1;
    return x;
}
void work()
{
    ll P=getPrime();
    cout<<"? 1 "<<P<<endl;
    ll r=read();
    if(!r)
    {
        cout<<"! "<<1<<' '<<P<<endl;
    }
    else if(r<2*P)//case1
    {
        ll a=r-P+1,b=P;
        ll t=__gcd(a,b);
        a=a/t;b=b/t;
        cout<<"! "<<a<<' '<<b<<endl;
    }
    else if(r%P+P<=1000000000&&r%P+P*(r%P+P)==r)//case2.2
    {
        cout<<"! "<<1<<' '<<r%P+P<<endl;
    }
    else//case2.1
    {
        ll q1=P-r%P;
        ll q2=2*P-r%P;
        ll ansp=(r+q1-q1*P)/P,ansq=q1;
        if(1<=ansp&&ansp<=ansq&&ansq<=1000000000)
            cout<<"! "<<ansp<<' '<<ansq<<endl;
        else
            cout<<"! "<<(r+q2-q2*P)/P<<' '<<q2<<endl;
    }
    read();
}
int main()
{
    srand((ll)time(0));
    for(int t=read();t;t--)
        work();
}
L

 

posted @ 2023-05-11 15:38  zzuqy  阅读(991)  评论(5编辑  收藏  举报