T1:小凯的疑惑
考察知识:数学,数论
算法难度:XXX 实现难度:X
分析:这是一个推(cai)结(da)论(an)的题
一看数据范围,就知道应该用时间复杂度或以下的算法,如果猜有些或许你会发现答案就是,注意用long long
关于结论的证明就参考这里吧:P3951 小凯的疑惑 题解
代码:
#include<iostream>
int main(){
long long a,b;
std::cin>>a>>b;
std::cout<<a*b-a-b;
return 0;
}
T2:时间复杂度
考察知识:模拟,栈
算法难度:XXX 实现难度:XXX+
分析:一个在NOIP中中等规模的模拟类题目
大致思路:我们采用栈结构动态储存每一层的时间复杂度,并在当前层更新时间复杂度。
这道题保证,更加简化了这道题的难度
代码:
#include<map>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
map<string,int>mp;
int T,L,top,cur[105];
char O[255],F[255],var[105][255],x[255],y[255];
void check(){
int index=0,index2=0,index_now=0;//时间复杂度指数
bool CE=false;
memset(cur,0,sizeof(cur));
top=0,cur[0]=1,mp.clear();
scanf("%d%s",&L,O);
for(int i=1;i<=L;i++){
scanf("%s",F);
if(F[0]=='F'){
scanf("%s%s%s",var[++top],x,y);
if(mp[var[top]]==2018) CE=true;//判断变量是否重名
else mp[var[top]]=2018;
int X=0,Y=0;//循环次数
if(x[0]=='n') X=2018;
else for(int i=0;x[i]!='\0';i++) X=X*10+x[i]-'0';
if(y[0]=='n') Y=2018;
else for(int i=0;y[i]!='\0';i++) Y=Y*10+y[i]-'0';
if(X>Y||cur[top-1]==0) cur[top]=0;//无法进入更深的循环
else if((X<=Y&&Y<2018)||X==Y) cur[top]=1;//当前时间复杂度O(1)
else cur[top]=2;//O(n)
// if(cur[top-1]==0) cur[top]=0;
if(cur[top]==2) index=max(index,++index_now);//当前最大时间复杂度
} else {
if(cur[top]==2) index_now--;
mp[var[top--]]=0;//销毁变量
}
}
if(top||CE) {printf("ERR\n");return;}
if(O[2]=='n'){
for(int i=4;isdigit(O[i]);i++) index2=index2*10+O[i]-'0';
}
if(index==index2) printf("Yes\n");
else printf("No\n");
}
int main(){
scanf("%d",&T);
while(T--) check();
return 0;
}
T3:逛公园
考察知识:图论,记忆化搜索
算法难度:XXXX 实现难度:XXXX
分析:
遇到难题先看数据范围,有3个的点,就是最短路径计数模板,所以我们至少可以得30分。
对于非搜索类难题,数据范围就算小也不一定想得出部分分算法,但是注意到这道题,我们可以考虑用记忆化的方法储存每个点比最短路大k的路径数,然后采用记忆化搜索即可。
这种方法类似于动态规划,所以我们还是用动态规划一般套路来描述:
定义:表示节点 i 到 n 所有路径中路径长度小于等于最短路径加 k 的路径数
状态转移:
设V是节点 i 可以直接到达的节点的集合,表示 i 到 n 的最短路径长度,表示节点 i,j 的距离
则有:
边界:至少等于1
注意事项:
1.由于有多组数据,每次处理完后要初始化
2.由于部分数据含0边,所以我们设一个数组,表示正在被计算,如果继续搜索又达到 (i,k)说明有无穷多种情况
代码:
#include<map>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char in_c;
template<typename T>
void scan(T &in_n){
for(in_c=getchar();in_c<'0'||in_c>'9';in_c=getchar());
for(in_n=0;in_c>='0'&&in_c<='9';in_c=getchar()) in_n=in_n*10+in_c-'0';
}
const int maxn=100005;
struct edge{
int to,next,l;
}e[maxn*2],e_[maxn*2];
int head[maxn],np,head_[maxn],np_;
void adde(int u,int v,int l){
e[++np]=(edge){v,head[u],l};
head[u]=np;
}
void adde_(int u,int v,int l){
e_[++np_]=(edge){v,head_[u],l};
head_[u]=np_;
}
int T,n,k,m,P,f[maxn][51],d[maxn];
bool vis[maxn],vising[maxn][51],err;
void dijstra(){//n->1反向搜索
priority_queue<pair<int,int> >pq;
memset(d,0x3f,sizeof(d));
memset(vis,0,sizeof(vis));
pq.push(make_pair(0,n)),d[n]=0;
int i,j,c;
while(!pq.empty()){
i=pq.top().second,pq.pop();
vis[i]=true;
for(int p=head_[i];p;p=e_[p].next){
j=e_[p].to,c=e_[p].l;
if(d[i]+c<d[j]){
d[j]=d[i]+c;
if(!vis[j]) pq.push(make_pair(-d[j],j));
}
}
}
}
int dp(int i,int K){
if(f[i][K]) return f[i][K];
if(vising[i][K]) err=true;
if(err) return 0;
vising[i][K]=true;
if(i==n) f[i][K]=1;//动态赋值
for(int p=head[i];p;p=e[p].next){
int j=e[p].to,c=e[p].l;
if(K>=d[j]+c-d[i]){
int tmp=dp(j,K-d[j]-c+d[i]);
f[i][K]=(f[i][K]+tmp)%P;
}
}
vising[i][K]=false;//修改结束
return f[i][K];
}
void build(){
int u,v,l;
np=np_=0,err=false;
memset(e,0,sizeof(e));
memset(f,0,sizeof(f));
memset(head,0,sizeof(head));
memset(vising,0,sizeof(vising));
memset(e_,0,sizeof(e_));
memset(head_,0,sizeof(head_));
scanf("%d%d%d%d",&n,&m,&k,&P);
for(int i=1;i<=m;i++)
scan(u),scan(v),scan(l),
adde(u,v,l),adde_(v,u,l);
}
void solve(){
dijstra();
dp(1,k);
printf("%d\n",err?-1:f[1][k]);
}
int main(){
scanf("%d",&T);
while(T--) build(),solve();
return 0;
}