2019-8-15 考试总结

A. 数论

数学题,经实践证明,这个题可以$AC$。

考试时打的暴力,拿到$20$分。

正解:

虽然现在思路还是有点模糊,但是大体的思路应该差不多。

首先,就像题解说的,如果对于一个非良好数$x$,$xp^c$也是非良好数,其中$p$为质数,$c>=0$。

前提是$x$中不含质因子$p$。

$xp^c$的约数个数$val[xp^c]$是$val[x] imes (c-1)$。

$x$中没有质因子$p$,当然他的约数中也没有质因子$p$,所以每次乘$p$都会形成新的约数。

之后就是一些玄学的操作。

每次"迭代"$($新名词$)$,把原序列中的数拓展,

先枚举素数,再枚举原序列中的数,再枚举指数,把新数存到临时的数组中。

最后删去新的临时数组中不合法的数,因为这个数不会再更新别的数了。

注意要排序。

再维护一个堆,维护前$k+1$大的约数个数。

这样就可以删掉当前不合法的数了,应该是当前,因为之后插进来的数还可能再使它不合法。

最后一定可以筛掉所有不合法的数。

最后$TLE70$。

丑陋的代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define _min(x,y) ((x)<(y)?(x):(y))
#define _max(x,y) ((x)>(y)?(x):(y))
#define _abs(x) ((x)<0?(-1*(x)):(x))
#define Maxn 1000050
#define Reg register
//#define int long long
#define int __int128
using namespace std;
bool vis[Maxn];
int T,K,m,tot,top[2],pri[Maxn],ans[Maxn];
struct Node {int x,cnt;} stack[2][Maxn],lss[Maxn];
bool comp(Node a,Node b) {return a.x<b.x;}
priority_queue<Node,vector<int>,greater<int> > q;
void print(int x)
{
    if(x>=10) print(x/10);
    putchar(x%10+48);
    return;
}
signed main()
{
    scanf("%lld%lld%lld",&T,&K,&m);
    for(Reg int i=2;i<=10000;++i)
    {
        if(vis[i]) continue;
        pri[++tot]=i;
        if(tot==300) break;
        for(Reg int j=2;j*i<=100000;++j) vis[j*i]=1;
    }
    stack[0][++top[0]]=(Node){1,1};
    for(Reg int i=1;i<=tot;++i)
    {
        int cur=i&1,pre=(i+1)&1;
        top[cur]=0;
        for(Reg int j=1;j<=top[pre];++j)
        {
            int lss=1,x=stack[pre][j].x,p=stack[pre][j].cnt;
            for(Reg int k=0;k<=60;++k)
            {
                if(lss*x>m||lss*x<0) break;
                stack[cur][++top[cur]]=(Node){lss*x,p*(k+1)};
                lss*=pri[i];
            }
        }
        sort(stack[cur]+1,stack[cur]+top[cur]+1,comp);
        int tok=0;
        while(!q.empty()) q.pop();
        for(Reg int j=1;j<=top[cur];++j)
        {
            if(stack[cur][j].x==1)
            {
                if(q.size()>=K+1&&stack[cur][j].cnt-1<q.top()) continue;
                lss[++tok]=stack[cur][j];
                q.push(stack[cur][j].cnt-1);
                while(q.size()>K+1) q.pop();
            }
            else
            {
                if(q.size()>=K+1&&stack[cur][j].cnt-2<q.top()) continue;
                lss[++tok]=stack[cur][j];
                q.push(stack[cur][j].cnt-2);
                while(q.size()>K+1) q.pop();
            }
        }
        top[cur]=0;
        for(Reg int j=1;j<=tok;++j) stack[cur][++top[cur]]=lss[j];
    }
//    for(Reg int i=1;i<=top[tot&1];++i) cout<<stack[tot&1][i].x<<endl;
    while(T--)
    {
        int x; scanf("%lld",&x);
        print(stack[tot&1][x].x); puts("");
    }
    return 0;
}
View Code

B. 位运算

正解:

用$dp[i][j]$表示第$i$个数,到这时一共有$j$个$1$,这种情况是否存在$(0/1)$。

记录一下前趋,因为只要一组合法解,所以出现一个记录一个就好了。

转移的话分三种情况:

符号为$&$,这时下界是$max(0,j+a[i+1]-m)$,上界是$min(j,a[i+1])$,

符号为$|$,这时下界是$max(j,a[i+1])$,上界是$min(m,j+a[i+1])$,

符号为^,这时下界是$|j-a[i+1]|$,上界是$2m-j-a[i+1]$。

就是这样,然后记录前趋。

最后用$dfs$还原原数,很麻烦,很多细节。

丑陋的代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#define _min(x,y) ((x)<(y)?(x):(y))
#define _max(x,y) ((x)>(y)?(x):(y))
#define _abs(x) ((x)<0?(-1*(x)):(x))
#define Maxn 100050
#define Reg register
int n,m,c,opt[Maxn],dp[Maxn][35],pre[Maxn][35],A[Maxn],num[Maxn];
char s[10];
int get(int x)
{
    int s=0;
    while(x) {if(x&1) ++s; x>>=1;}
    return s;
}
void dfs(int now,int st)
{
    if(now==1) {num[1]=st; return;}
    if(opt[now-1]==1)
    {
        int nxt=pre[now][get(st)],nst=st,npt=st;
        for(Reg int i=1;i<=m;++i)
        {
            if(get(nst)==nxt) break;
            if(!(nst&(1<<(i-1)))) nst|=(1<<(i-1));
        }
        for(Reg int i=1;i<=m;++i)
        {
            if(get(npt)==A[now]) break;
            if(((st&(1<<(i-1)))&&!(nst&(1<<(i-1))))) npt|=(1<<(i-1));
            else if((!(st&(1<<(i-1)))&&!(nst&(1<<(i-1))))) npt|=(1<<(i-1));
        }
        num[now]=npt; dfs(now-1,nst);
    }
    else if(opt[now-1]==2)
    {
        int nxt=pre[now][get(st)],nst=st;
        for(Reg int i=1;i<=m;++i)
        {
            if(get(nst)==nxt) break;
            if(nst&(1<<(i-1))) nst^=(1<<(i-1));
        }
        int npt=nst^st;
        for(Reg int i=1;i<=m;++i)
        {
            if(get(npt)==A[now]) break;
            if(nst&(1<<(i-1))) npt|=(1<<(i-1));
        }
        num[now]=npt; dfs(now-1,nst);
    }
    else
    {
        int nxt=pre[now][get(st)],nst=0,npt=0;
        for(Reg int i=1;i<=m;++i)
        {
            if(!(st&(1<<(i-1)))) continue;
            if(get(nst)<nxt) nst|=(1<<(i-1));
            else npt|=(1<<(i-1));
        }
        for(Reg int i=1;i<=m;++i)
        {
            if(nxt-get(nst)==A[now]-get(npt)) break;
            if(nxt-get(nst)>A[now]-get(npt)&&(npt&(1<<(i-1)))&&!(nst&(1<<(i-1)))) nst^=(1<<(i-1)),npt^=(1<<(i-1));
            if(nxt-get(nst)<A[now]-get(npt)&&(nst&(1<<(i-1)))&&!(npt&(1<<(i-1)))) nst^=(1<<(i-1)),npt^=(1<<(i-1));
        }
        for(Reg int i=1;i<=m;++i)
            if(!(st&(1<<(i-1)))&&get(nst)<nxt&&get(npt)<A[now]) nst|=(1<<(i-1)),npt|=(1<<(i-1));
        num[now]=npt; dfs(now-1,nst);
    }
    return;
}
int main()
{
    scanf("%d%d%d",&n,&m,&c);
    for(Reg int i=1;i<=n-1;++i)
    {
        scanf("%s",s);
        if(s[0]=='A') opt[i]=1;
        else if(s[0]=='O') opt[i]=2;
        else opt[i]=3;
    }
    for(Reg int i=1;i<=n;++i) scanf("%d",&A[i]);
    dp[1][A[1]]=1;
    for(Reg int i=1;i<=n-1;++i)
    {
        for(Reg int j=0;j<=m;++j)
        {
            if(!dp[i][j]) continue;
            if(opt[i]==1) for(Reg int k=_max(0,j-(m-A[i+1]));k<=_min(j,A[i+1]);++k) dp[i+1][k]=1,pre[i+1][k]=j;
            else if(opt[i]==2) for(Reg int k=_max(j,A[i+1]);k<=_min(m,j+A[i+1]);++k) dp[i+1][k]=1,pre[i+1][k]=j;
            else
            {
                int mxx=(m-A[i+1]>j)?(A[i+1]+j):(2*m-j-A[i+1]);
                for(Reg int k=_abs(j-A[i+1]);k<=mxx;k+=2) dp[i+1][k]=1,pre[i+1][k]=j;
            }
        }
    }
    if(!dp[n][get(c)]) printf("OvO");
    else
    {
        dfs(n,c);
        for(Reg int i=1;i<=n;++i) printf("%d ",num[i]);
    }
    return 0;
}
View Code

C. 旅行

总结:

这次考试题目挺好的,做题改题都挺费劲的。

其实这次考试也没必要谈心态,因为心态好了也想不出来。。。

说考试过程吧,

前$40$分钟看$T1$,开始打$k=0$的表,企图能找到一些规律。

发现前几项非常像等差数列。。其实没什么规律。

而且暴力分给的非常少。

之后看$T2$,发现居然有暴力分。。

打完搜索,然后看特殊性质$1$,应该能拿到一些分。

然后码了一个小时左右,嗯,以为$T2$的暴力分能拿满。

最后$T3$,心想可能教练故意难度递减排序,应该可做,然后想了一会儿,还是不可做。。

滚回去看$T1$,依然没什么思路,快速码了一个暴力,水到$20$分。

最后$20+10+0=30$。

没什么水平。。。

原文地址:https://www.cnblogs.com/Milk-Feng/p/11360753.html