T1

读入字符串,替换''即可,其他按原样输出

参考代码

#include
using namespace std;
string a;
int main()
{
    getline(cin,a);
    for(int i=0;i<a.length();i++)
    {
        if(a[i]=='<') cout<<"&lt;";
        else if(a[i]=='>') cout<<"&gt;";
        else cout<<a[i];
    }
    return 0;
}

T2

简化题意:
n个数相乘得到数g,求g二进制下后导0的个数

考虑把n个数都换成二进制,模拟二进制相乘即可发现规律
答案即是n个数的二进制下后导0的个数相加

证明

我们假设2个数相乘,分别是20,22
首先将他们2个转为二进制

  • 20:10100
  • 22:10110

我们再模拟二进制下的相乘
二进制下的相乘
可以发现得出的数的二进制下后导0即是相乘的两个数的二进制下后导0数量之和

参考代码

#include
using namespace std;
int n,x,ans;
int main()
{
    cin>>n;
    while(n--){
        scanf("%d",&x);
        while(x) (x&1)?x=0:ans++,x>>=1;
    }
    cout<<ans<<"\n";
    return 0;
}

T3

前置知识

  • 杨辉三角形与组合数的关系

  • 线性求逆元

    分析

    杨辉三角形的第n行的m个数可表示为 C(n-1,m-1),即为从n-1个不同元素中取m-1个元素的组合数。
    其实就是先预处理(预处理阶乘和逆元的阶乘),然后O(1)求组合数再乘起来取模即可

    参考代码

    #include
    using namespace std;
    const int N=1e6+1,mod=1e9+7;
    unsigned int C[N],g[N];
    int main(){
      int n,ans=1;
      scanf("%d",&n);
      C[0]=g[0]=g[1]=1;
      for(int i=2;i<n/2+1;++i)g[i]=(1ULL*g[mod%i]*(mod-mod/i))%mod;
      for(int i=1;i<=n/2+1;++i)C[i]=(1ULL*((1ULL*C[i-1]*(n-i))%mod)*g[i])%mod;
      for(unsigned int i=1;i<=n/2;++i)ans=(1ULL*ans*C[i-1])%mod;
      ans=1ULL*ans*ans%mod;
      if(n&1) ans=1ULL*ans*C[n/2]%mod;
      printf("%d",ans);
      return 0;
    }

    T4

    哈夫曼树模板题
    特判一下只有一种字母的情况(虽然测试数据并没有这种情况QWQ)

参考代码

#include
using namespace std;
string a;
int ans,t[200];
priority_queue v;
signed main()
{
    cin>>a;
    for(int i=0;i<a.length();i++) t[a[i]]++;
    for(int i='a';i<='z';i++) if(t[i]) v.push(-t[i]);
    if(v.size()==1) 
        return printf("%d\n",a.length())&0;
    while(v.size()!=1){
        int k=0;
        k+=v.top();v.pop();
        k+=v.top();v.pop();
        v.push(k);ans+=k;
    }
    cout<<-ans<<"\n";
    return 0;
}

T5

前置知识:

  • 勾股定理
  • 海伦公式
  • 空间向量
  • 三棱锥体积公式

推理

我们知道四面体即是三棱锥
通过 底即可得到体积
我们用前3个点构成的面当作底面
用海伦公式求底面积,调用
用空间向量求高,首先求出底面的法向量,在底面上随便找一个点连向第4个点构成向量,求此向量在法向量上的投影即是高

参考代码

#include
using namespace std;
struct Vector{
    int x[5];
}t[10],a,b,c,d;
double h;
int main()
{
    for(int i=0;i<4;i++)
        for(int j=0;j<3;j++)
            cin>>t[i].x[j];
    for(int i=0;i<3;i++){
        a.x[i]=t[1].x[i]-t[0].x[i];
        b.x[i]=t[2].x[i]-t[0].x[i];
        d.x[i]=t[3].x[i]-t[0].x[i];
    }
    for(int i=0;i<3;i++)
        c.x[i]=a.x[(i+1)%3]*b.x[(i+2)%3]-b.x[(i+1)%3]*a.x[(i+2)%3];
    int cd=0;
    for(int i=0;i<3;i++)
        cd+=c.x[i]*d.x[i];
    h=fabs(cd/sqrt(c.x[0]*c.x[0]+c.x[1]*c.x[1]+c.x[2]*c.x[2]));
    double k[4],p,s;
    for(int i=0;i<3;i++)
        k[i]=sqrt((t[i].x[0]-t[(i+1)%3].x[0])*(t[i].x[0]-t[(i+1)%3].x[0])+(t[i].x[1]-t[(i+1)%3].x[1])*(t[i].x[1]-t[(i+1)%3].x[1])+(t[i].x[2]-t[(i+1)%3].x[2])*(t[i].x[2]-t[(i+1)%3].x[2]));
    p=(k[0]+k[1]+k[2])/2;
    s=sqrt(p*(p-k[0])*(p-k[1])*(p-k[2]));
    printf("%.1lf\n",s*h/3);
    cout<<s<<"\n";
    cout<<h<<"\n";
    return 0;
}

T6

先求一遍前缀和数组为h
需要求满足 && 的式子
题目就转为求前缀和数组中逆序对的数目
使用归并排序求逆序对
或使用高级数据结构维护(如平衡树)

参考代码(归并排序)

#include
using namespace std;
long long n;
long long h[1000008]; 
long long a[1000008];
long long c[1000008];
long long ans;
void fsort(long long l,long long r)
{
    if(l>=r) return ;
    long long tot=0;
    long long mid=(l+r)/2;
    long long aa=l,bb=mid+1;
    fsort(l,mid);
    fsort(mid+1,r);
    while(aa<=mid&&bb<=r){
        if(a[aa]<a[bb]){
            c[++tot]=a[aa++];
            ans+=(r-bb+1);
        }
        else{
            c[++tot]=a[bb++];
        }
    }
    while(aa<=mid)
        c[++tot]=a[aa++];
    while(bb<=r)
        c[++tot]=a[bb++];
    for(long long i=l,j=1;i<=r;i++,j++)
        a[i]=c[j];
}
int main()
{
    scanf("%lld",&n);
    a[1]=h[1]=0;
    n++;
    for(long long i=2;i<=n;i++){
        scanf("%lld",&h[i]);
        a[i]=a[i-1]+h[i];
    }
    fsort(1,n);
    cout<<ans<<"\n";
    return 0;
}

参考代码(Splay树)

#include
#define ll long long
#define int long long
#define inf INT_MAX
#define lc(x) s[x].ch[0]
#define rc(x) s[x].ch[1]
using namespace std;
const int N=1e6+10;
int n,rt,tot;
int a[N];
struct nkl {
    int ch[2],siz,cnt,val,fa;
} s[N];
inline int ident(int x,int y) {
    return (rc(y)==x);
}
inline void cn(int x,int y,int k) {
    s[x].fa=y;
    s[y].ch[k]=x;
}
inline void upd(int x) {
    s[x].siz=s[lc(x)].siz+s[rc(x)].siz+s[lc(x)].cnt+s[rc(x)].cnt;
}
inline void rotate(int x) {
    int y=s[x].fa,z=s[y].fa,k=ident(x,y);
    cn(s[x].ch[k^1],y,k);
    cn(x,z,ident(y,z));
    cn(y,x,k^1);
    upd(y),upd(x);
}
inline void splay(int x,int to) {
    if(!to) rt=x;
    while(s[x].fa!=to) {
        int y=s[x].fa,z=s[y].fa;
        if(z!=to)
            ident(x,y)^ident(y,z) ? rotate(x):rotate(y);
        rotate(x);
    }
}
inline void newcode(int &now,int x,int fa) {
    now=++tot;
    s[now].val=x;
    s[now].fa=fa;
    s[now].cnt++;
}
inline void ins(int &now,int x,int fa) {
    if(!now) newcode(now,x,fa),splay(now,0);
    else if(s[now].val<x) ins(s[now].ch[1],x,now);
    else if(s[now].val>x) ins(s[now].ch[0],x,now);
    else s[now].cnt++,splay(now,0);
}
signed main() {
    scanf("%lld",&n);
    for (int i=1; i<=n; i++)
        scanf("%lld",&a[i]),a[i]+=a[i-1];
    ll ans=0;
    for (int i=1; i<=n; i++) {
        ins(rt,a[i],0);
        ans+=s[lc(rt)].siz+s[lc(rt)].cnt;
        if(a[i]>0) ans++;
    }
    printf("%lld\n",ans);
    return 0;
}

T7

题目灵感来自现实,首先某OJ相信大家都看出来是哪个网站了,实际上它真的做了这个非常奇怪的js校验。当然这个校验的反爬虫能力其实并不强,你只需要在爬虫里面去计算这个hash值就行了。(我猜测它并不是真的为了防止爬虫,而是避免直接用Python利用Requests库短时间发送大量请求,这样的话你至少需要计算hash来拖时间)这道题的数据范围就是真实的数据范围,所以算法很有趣对吧,说不定就用上了。

题解

题意是让你去算一个字符串的反哈希,哈希的取值范围是[0,1008]。注意到取值范围很小,所以可以通过大量随机撞库获取到一个字符串到哈希值的映射表(被称为彩虹表)的东西,然后直接查询就可以了。

T8

题目灵感来自现实,首先某OJ相信大家都看出来是哪个网站了,实际上它真的做了这个非常奇怪的隐藏表单。当然这个隐藏的反爬虫能力其实并不强,你只需要在爬虫里面去获取这个隐藏表单值就行了。这道题的数据是真实的数据上稍加改动,所以算法很有趣对吧,说不定就用上了。

题解

题意是让你去找一个字符串,抓住字符串特征,字符串匹配即可

T9

模拟即可

参考代码

#include
using namespace std;
string t;
int a[105];
int tot;
queue v[4];
string cname[3]={"xiran","sanqiu"};
string pai[24]={"","A","2","3","4","5","6","7","8","9","10","J","Q","K"};
stack q;
bool flag[21];
void get(int k,int x)
{
    int all=2;
    while(q.size()&&all){
        v[x].push(q.top());
        flag[q.top()]=0;
        if(q.top()==k)
            all--;
        q.pop();
    }
}
void work(int x)
{
    int k=v[x].front();
    v[x].pop();
    q.push(k);
    if(k==11){
        if(q.size()==1)
            return; 
        else{
            get(0,x);
            work(x);
        }
    }
    else{
        if(flag[k]){
            get(k,x);
            work(x);
        }
        else
            flag[k]=1;
    }
}
int main()
{
    cin>>t;
    for(int i=0;i<=t.size();i++){
        if(t[i]=='A') a[++tot]=1;
        else if(t[i]>='2'&&t[i]<='9') a[++tot]=t[i]-'0';
        else if(t[i]=='1') {a[++tot]=10;i++;}
        else if(t[i]=='J') a[++tot]=11;
        else if(t[i]=='Q') a[++tot]=12;
        else if(t[i]=='K') a[++tot]=13;
    }
    for(int i=1;i<=tot;i++) v[i&1].push(a[i]);
    int win=0;
    while(1){
        work(1);
        if(v[1].size()==0) {win=0;break;}//判断游戏是否结束 
        work(0);
        if(v[0].size()==0) {win=1;break;}//判断游戏是否结束 
    }
    cout<<cname[win]<<"\n";
    while(v[win].size()){
        cout<<pai[v[win].front()]<<" ";
        v[win].pop();
    }
    return 0;
}

T10

证明

我们先定义2个集合

  • 集合A 代表溪染拥有的所有优惠券的集合
  • 集合B 为集合A的子集

如果我们使用集合B里面的优惠券,无论我们如何顺序使用(前提是所有优惠券能够使用)
集合B带来的优惠总额是不变的,考虑以不同的顺序使用,会对什么造成影响?

再定义

  • 排列P 为集合B里优惠券的某种排列
  • 金钱K 为若按照排列P使用优惠券,保证所有优惠券可以使用的情况下,商品最初最少为多少元
  • 按照排列P,优惠券的参数

我们来看一看金钱K需要满足什么式子,如下:

  • ......

再定义

  • 最优排列 对于一个集合B有很多排列P,当某一个排列P金钱K最小时,我们称其为最优排列

不难证明对于最优答案的合法排列总有一个是最优排列(证明略)

现在问题转为如何求集合B的最优排列
可以证明,当按照下列式子排序后,得到的就是最优排序

证明可以参考 [NOIP2012 提高组] 国王游戏 的证明方法


不难证明集合B的最优排列集合A的最优排列的子序列(证明略)
那么这道题就十分明了了,我们先计算出集合A的最优排列
再考虑选取集合B,由于我们已经得到了最优排序
我们就不必再考虑使用顺序的问题(按照最优排序使用即可)
此时问题已经变成了01背包问题,按照规则构建01背包DP方程即可, 表示购买价格为i的商品最少需要多少钱
注意,01背包枚举优惠券的顺序需要按照顺序枚举!
DP枚举时,先枚举的是最后选的,所以按照集合A的最优排列的倒序枚举
至此,此题完

参考代码

#include
#define N 100005
using namespace std;
int n,k;
struct oppo{
    int a,b;
}t[N];
bool rule(oppo a,oppo b){
    return a.a-a.b>b.a-b.b;
}
int dp[1000005];
int main()
{
    cin>>n>>k;
    for(int i=1;i<=k;i++) dp[i]=i;
    for(int i=1;i<=n;i++)
        scanf("%d%d",&t[i].a,&t[i].b);
    sort(t+1,t+n+1,rule);
    for(int i=n;i>=1;i--)
        for(int j=k;j>=t[i].a;j--)
            dp[j]=min(dp[j],dp[j-t[i].b]);
    cout<<dp[k]<<"\n";
    return 0;
}