胖爷XP的hu测 T3.程序(提答)

61 篇文章 0 订阅
10 篇文章 0 订阅

版权属于XP,想要引用此题(包括题面)的朋友请联系博主

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
分析:

为什么我要在blog里写提答题:感觉这次的提答比较好,考核的知识点很好
dada们就当玩笑看看吧

一道一道看

program1

#include<bits\stdc++.h>
using namespace std;
int getseed(char *s)
{
    int seed=0;
    int len=strlen(s);
    for(int i=0;i<len;i++)
    seed=seed*27+s[i]-'a'+1;
    return seed;
}
int n;
char s[101][1001];
map<int,int>ma;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%s",s[i]);
    int ans=n;
    for(int i=1;i<=n;i++)
    {
        int sed=getseed(s[i]);
        if(ma[sed]) ans--;
        else ma[sed]++;
    }
    cout<<ans;
}

int自然溢出的hash,相当于% 232 2 32
这里写图片描述
最简单的,我们考虑找到一个和 a ′ a ′ 同余的字符串
那么这个字符串的hash值应该是 232+1=4294967297 2 32 + 1 = 4294967297
把这个数转成27进制即可
(注意由于字符串中没有表示0的字符,所以如果在转换的时候出现了0,就需要换一个数,然而出题人良心并不存在这个问题)

//可行解
2
a
kbhsymw

(当然还可以暴力构造)

//可行解
2
enqwlth
ppzpkgc

program2

#include<bits\stdc++.h>
using namespace std;
typedef pair<int,int> pi;
pi getseed2(char *s)
{
    long long seed1=0,seed2=0;
    int len=strlen(s);
    for(int i=0;i<len;i++)
    {
        seed1=seed1*27+s[i]-'a'+1;
        seed1%=19260817;
        seed2=seed2*27+s[i]-'a'+1;
        seed2%=998244353;
    }
    return pi(seed1,seed2);
}
int n;
char s[101][1001];
map<pi,int>mp;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%s",s[i]);
    int ans=n;
    for(int i=1;i<=n;i++)
    {
        pi sed=getseed2(s[i]);
        if(mp[sed]) ans--;
        else mp[sed]++;
    }
    cout<<ans;
}

双hash
简单的,我们考虑如何构造一个和 a ′ a ′ hash相同的字符串卡掉上面的程序
我们需要找到一个%两个模数答案都等于0的数字(+1就可以与 a ′ a ′ 同余了)
换句话说,我们需要找到两个模数的共同倍数: lcm l c m 即可(直接把两个模数乘起来)

转换成27进制
(还是注意由于字符串中没有表示0的字符,所以如果在转换的时候出现了0,就需要换一个数,然而出题人良心并不存在这个问题)

//可行解
2
a
cljjdkjejxuf

program3

#include<bits\stdc++.h>
using namespace std;
int num;
int main()
{
    scanf("%d",&num);
    int p=1;
    int sz=sqrt(num);
    for(int i=2;i<sz;i++)
    if(num%i==0) p=0;
    if(p) puts("Yes");
    else puts("No");
}

显然质数平方就可以卡掉这个代码

//可行解
10201

program4

#include<bits\stdc++.h>
using namespace std;
int num;
int quickpow(int x,int p)
{
    int ans=1;
    for(int t=p-1;t;t>>=1,x=x*x%p)
    if(t&1) ans=ans*x%p;
    return ans;
}
int gcd(int x,int y)
{
    return y==0?x:gcd(y,x%y);
}
int main()
{
    scanf("%d",&num);
    int p=1;
    for(int i=2;i<num;i++)
    if(quickpow(i,num)!=1&&gcd(i,num)==1) p=0;
    if(p) puts("Yes");
    else puts("No");
}

考察伪素数
我们naive的扫一遍即可

//可行解
10585

program5

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef pair<int,int> IntPair;
typedef vector<IntPair> VectorIntPair;
#define INF 1000000000
int i,j,u,V,n,w,Q,time_up,s,t;
vector<VectorIntPair> AdjList;
bool change;
int main() {
  scanf("%d",&V);
  AdjList.assign(V,VectorIntPair());
  for(i=0;i<V;i++) {
    scanf("%d",&n);
    while(n--){
      scanf("%d%d",&j,&w);
      AdjList[i].push_back(IntPair(j,w));
    }
  }
  time_up=0;
  scanf("%d",&Q);
  while(Q--){
    scanf("%d%d",&s,&t);
    vector<int>dist(V,INF);
    dist[s]=0;

    for (i=0;i<V-1;i++) {
      change=false;
      for (u=0;u<V;u++)
        for (j=0;j<(int)AdjList[u].size();j++){
          time_up++;
          if (time_up>1000000){
            printf("TLE");
            return 0;
          } 
          IntPair v=AdjList[u][j];
          if (dist[u]+v.second<dist[v.first]){
            dist[v.first]=dist[u]+v.second;
            change=true;
          }
        }
      if (!change)
        break;
    }
    printf("%d\n",dist[t]);
  }
  printf("time_up=%d\n",time_up);
  return 0;
}

bellman ford算法
这题的字符限制比较宽松,观察程序特点可以看出:for (j=0;j<(int)AdjList[u].size();j++)
这个程序是从0~n-1依次枚举边进行松弛,然后如果有一次没有松弛操作则退出

那么我们可以利用这个缺陷,第一次在 n1 n − 1 松弛,第二次在 n2 n − 2 松弛,以此类推
然后让程序把大量无用操作放在前面节点的重边和自环上

实现方式:从0~299依次连最短路,然后询问的时候从299询问到0(其中加上一些重边)

program6

#include<algorithm>
#include<cstdio>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
typedef pair<int,int> IntPair;
typedef vector<IntPair> VectorIntPair;
#define INF 1000000000
int i,j,V,n,w,time_up,Q,s,t,d,u;
vector<VectorIntPair> AdjList;
int main() 
{
  scanf("%d",&V);
  AdjList.assign(V,VectorIntPair());
  for (i=0;i<V;i++) {
    scanf("%d",&n);
    while(n--){
      scanf("%d%d",&j,&w);
      AdjList[i].push_back(IntPair(j, w));
    }
  }
  time_up=0;
  scanf("%d",&Q);
  while(Q--){
    scanf("%d%d",&s,&t);
    vector<int>dist(V,INF);
    dist[s]=0;
    priority_queue<IntPair,vector<IntPair>,greater<IntPair> >pq;
    pq.push(IntPair(0,s));
    while(!pq.empty()){
      time_up++;
      if (time_up>1000000){
        printf("TLE");
        return 0;
      }
      IntPair front=pq.top();pq.pop();
      d=front.first;u=front.second;
      if (d==dist[u]) {
        for(j=0;j<(int)AdjList[u].size();j++) {
          IntPair v=AdjList[u][j];
          if (dist[u]+v.second<dist[v.first]) {
            dist[v.first]=dist[u]+v.second;
            pq.push(IntPair(dist[v.first],v.first));
          }
        }
      }
    }
    printf("%d\n",dist[t]);
  }

  printf("time_up=%d\n",time_up);
  return 0;
}

堆优dij,在 n<=300 n <= 300 的情况下很难卡
然而ta毕竟是一个dijkstra,我们可以通过设置负权边的方式来卡

这里写图片描述
可以看到,dijkstra面对这种图出现了重复的无用操作
这里写图片描述
(不同颜色代表不同的更新路径)
可以发现在出现若干以上图嵌套的情况下,可以把dij的效率卡成指数级
但是我们有字符限制
我们可以分析一下,把10组询问问满,需要21个字符,点数量需要1个
构建一个这样的图需要两个点,三条边,边占两个字符,点占一个字符,共计8个,最后还有一个点的出度为0,再占一个字符

143-21-1-1=120 正好可以构建15组

但是当你愉快地构建了15组以后,你发现time_up≈980000
我们进行微调,去掉4组询问,改为加一组图就可以了

//可行解
33
2 1 -1 2 -2
1 2 -999999
2 3 -1 4 -2
1 4 -499999
2 5 -1 6 -2
1 6 -249999
2 7 -1 8 -2
1 8 -124999
2 9 -1 10 -2
1 10 -62499
2 11 -1 12 -2
1 12 -31249
2 13 -1 14 -2
1 14 -15624
2 15 -1 16 -2
1 16 -7812
2 17 -1 18 -2
1 18 -3906
2 19 -1 20 -2
1 20 -1953
2 21 -1 22 -2
1 22 -976
2 23 -1 24 -2
1 24 -488
2 25 -1 26 -2
1 26 -244
2 27 -1 28 -2
1 28 -122
2 29 -1 30 -2
1 30 -61
2 31 -1 32 -2
1 32 -30
0
6
0 32
0 32
0 32
0 32
0 32
0 32

彩蛋

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值