版权属于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依次枚举边进行松弛,然后如果有一次没有松弛操作则退出
那么我们可以利用这个缺陷,第一次在
n−1
n
−
1
松弛,第二次在
n−2
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