• 2017 NOIp提高组 DAY1 试做


    感觉这年的相对上午做的那个不正经的NOIp2019好的多啊

    D1T1 小凯的疑惑

    题目描述

    下方传送门
    题目链接
    上方传送门

    思路分析

    • 第一眼看上去就想用 (exgcd) 做,但眉头一皱,这可是 (D1T1) ,所以果断打表
    • 然后随便抓几个小数据你就会发现满足这样的规律:(ans = a*b-a-b),码量堪比 A+B problem
    • 看题解有的人进行了一波证明,还是很好理解的

      1.假设这个k它真的可以被表示为 (m*a+n*b) (玄学的思路)
      2.在有关 (a,b) 的条件不变的情况下,那 (m)(n) 一定不是普通数!不然如何凑出一个原本凑不出来的数?原题中 (m,n) 为正整数,为打破这一规则,不妨设 (n) 为负整数。此时为了 (k) 最大,(n=-1)
      3.代入 (n=-1,k=m*a-b)。由第一种思路的第二条,(b*a-b) 能被 (b) 表示出来,为了让它不能被表示出来,又要 (m) 最大,所以 (m=b-1)
      4.代入 (m=b-1),原式的最大值就等于: (a*b-a-b)

    Code

    //看起来多只是因为有些东西懒得删了
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define int long long
    using namespace std;
    inline int read(){
    	int x=0,f=1;
    	char ch=getchar();
    	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	return x*f;
    }
    int a,b;
    signed main(){
    	a = read(),b = read();
    	printf("%lld
    ",a*b-a-b); //是的没错,就这,没了
    	return 0;
    }
    

    D1T2 时间复杂度

    题目描述

    下方传送门
    题目描述
    上方传送门

    思路分析

    • 一眼看上去就知道是个模拟题,非常不友好
    • 最恶心的就是这题的输入,于是从题解里得知有一种叫 sscanf 的东西,而且还学到了 scanf 的一些奇淫巧技
    • 接下来模拟循环过程即可:
      • 对于ERR的情况,很简单,就两种
        1. F和E失配
        2. 变量名冲突
      • 可以过编译的话,直接判定时间复杂度就行了,无非就是 (O(1))(O(n))
        1. (O(1)) 的话,要么两个循环变量都是常数,要么就是直接没跑,另外起止都是 (n) 也是,这个点很容易忽略
        2. (O(n)) 的话,那就是出现 (n) 且循环可以跑了
      • 另外就是循环之间的关系,有并列的,有嵌套的,对于嵌套的时间度才会叠加
    • 上面处理好以后用栈维护一下就行了

    详见代码

    Code

    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define N 110
    using namespace std;
    int n,pro,top,pown,sbxm;//sbxm表示小明的答案,pro表示正确答案,pown表示n的指数
    bool bre[N],is_n[N],CE;//bre即break,判断循环是否终止;is_n标记当前层的时间复杂度是不是O(n)
    char s[N],sta[N];
    int main(){
    	int t;scanf("%d",&t);
    	while(t--){
    		scanf("%d%s",&n,s);
    		if(s[2]=='1')sbxm = 0;
    		else sscanf(s,"O(n^%d)",&sbxm);//sscanf用法自行百度
    		pro = top = pown = 0;
    		CE = 0;
    		for(int i = 1;i <= n;i++){
    			scanf("
    %[^
    ]",s);//表示如果不遇到换行符就全部输入进去
    			if(CE)continue;
    			if(s[0]=='E'){
    				if(!top)CE = 1;
    				else if(is_n[top--])pown--;//循环终止后下面的就不能再嵌套了,所以pown--
    			}else{
    				sta[++top] = s[2];//循环变量都放到一起
    				int len = strlen(s);
    				for(int i = 1;i < top;i++)if(sta[i]==s[2])CE = 1;
    				bre[top] = bre[top-1]||(s[4]=='n'&&s[len-1]!='n');
    				if(s[4]!='n'&&s[len-1]!='n'){
    					int a,b;
    					sscanf(s,"%*s%*s%d%d",&a,&b);//*s表示过滤掉,这里相当于又读取了一遍当前行
    					if(a>b)bre[top] = 1;
    				}
    				if(s[4]!='n'&&s[len-1]=='n')is_n[top] = 1,pown++;
    				if(!bre[top])pro = max(pro,pown);
    			}
    		}
    		if(top)CE=1;
    		if(CE)puts("ERR");
    		else puts(pro==sbxm?"Yes":"No");
    	}
    	return 0;
    }
    

    D1T3 逛公园

    题目描述

    下方传送门
    题目链接
    上方传送门

    思路分析

    • 题意很明确,先求最短路,再求出路径长度在一定区间范围内的方案数。
    • 目测是个稀疏图,按理说跑 (spfa) 问题应该不大,但在UOJ上被卡了就跑了一遍 (Dijkstra)
    • 剩下就是求方案数了,然而我打了一遍暴力一直 (MLE) 出锅,所以考虑正解
    • 不难发现 (K) 值最大也就是 (50) ,完全可以将差值为 (1)~(50) 的路线直接统计一遍,而没必要记录所有路径长
    • 所以只需要枚举一下差值,看如果是该差值能不能从终点回到最初的起点即可。
    • 另外这题还需要判 (0) 环,只需要将出发状态标记一下就好,如果又回去了就说明有 (0)

    Code

    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #define N 200010 
    using namespace std;
    inline int read(){
    	int x=0,f=1;
    	char ch=getchar();
    	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	return x*f;
    }
    int n,m,k,p,head[N],dis[N],ans,head2[N],f[N][66];
    bool vis[N],flag[N][66];
    struct edge{
    	int to,next,dis;
    }e[N],e2[N];
    int len,len2;
    void Init(){
    	memset(head,0,sizeof(head));
    	memset(head2,0,sizeof(head2));
    	memset(f,-1,sizeof(f));
    	memset(flag,0,sizeof(flag));
    	len = ans = len2 = 0;
    }
    void addedge(int u,int v,int w){
    	e[++len].to = v;
    	e[len].next = head[u];
    	e[len].dis = w;
    	head[u] = len;
    }
    void add2(int u,int v,int w){
    	e2[++len2].to = v;
    	e2[len2].next = head2[u];
    	e2[len2].dis = w;
    	head2[u] = len2;
    }
    struct node{
    	int dis,num;
    	node(){}
    	node(int _dis,int _num){dis=_dis,num=_num;}
    	bool operator <(const node &a)const{
    		return dis>a.dis;
    	}
    };
    void Dij(int x){
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	priority_queue<node>q;
    	dis[x] = 0;
    	q.push(node(0,x));
    	while(!q.empty()){
    		node p=q.top();q.pop();
    		int u = p.num;
    		if(vis[u])continue;
    		vis[u] = 1;
    		for(int i = head[u];i;i=e[i].next){
    			int v = e[i].to;
    			if(dis[v]>dis[u]+e[i].dis){
    				dis[v] = dis[u]+e[i].dis;
    				q.push(node(dis[v],v));
    			}
    		}
    	}
    }
    int dfs(int u,int dc){ //跑反图求方案数
    	if(dc<0||dc>k)return 0;
    	if(flag[u][dc])return -1; //出现0环
    	if(~f[u][dc])return f[u][dc]; //记忆化
    	flag[u][dc] = 1;
    	int res = 0;
    	for(int i = head2[u];i;i = e2[i].next){
    		int v = e2[i].to;
    		int tmp = dfs(v,dc+dis[u]-dis[v]-e2[i].dis); //计算最初差值的消耗量
    		if(tmp==-1)return -1;
    		res = (res+tmp)%p;
    	}
    	if(u==1&&!dc)++res; //回到起点,且差值正好用完,说明该路径合法
    	f[u][dc] = res;
    	flag[u][dc] = 0;
    	return res;
    }
    int main(){
    	int t;t =read();
    	while(t--){
    		Init();
    		n = read(),m = read(),k = read(),p = read();
    		for(int i = 1;i <= m;i++){
    			int a,b,c;a =read(),b = read(),c = read();
    			addedge(a,b,c);
    			add2(b,a,c);
    		}
    		Dij(1);
    		bool flag = 0;
    		for(int i = 0;i <= k;i++){
    			int tmp = dfs(n,i);
    			if(tmp==-1){
    				flag = 1;
    				break;
    			}
    			ans = (ans+tmp)%p;
    		}
    		if(flag)puts("-1");
    		else printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    总结

    • (T1) 还是一如既往地找规律,像这种类型的打表最方便不过了
    • (T2) 竟然考了个模拟,相当的恶心
    • (T3) 是个最短路加 (DP) 混合的题,关键在于后者的状态定义和遍历的方法
    • 总体来说难度还算是比较适中的,当然想做对并不容易。正在尝试找到更多套路性的东西

    另附洛谷 2017NOIp提高组题单->NOIp2017

  • 相关阅读:
    CF1091E
    jzoj5703
    CF1109F
    杂题
    CF1194F
    杂题
    个人作业1-数组(二维数组)
    第三周-学习进度条
    构建之法阅读笔记02
    个人作业1-数组(续1)
  • 原文地址:https://www.cnblogs.com/hhhhalo/p/13509448.html
Copyright © 2020-2023  润新知