• 题解 noip2018模拟测试赛(三十一)


    传送门

    国旗计划

    题目思路

    首先发现环很难搞,尝试破环为链,将长度为 (m)​​ 的环变成长度为 (2m)​​​ 的链

    然后我们发现对于当前选择了区间 ([l_i,r_i])​​​​​​​ 的情况来说,下一个选择的区间 ([l_d,r_j])​​​​​​​ 必定在所有满足 (l_jle r_i)​​​ 的区间中 (r_j)​​​​ 最大的

    又因为区间之间两两互不包含,所以 (r_j)​​ 最大的区间必定是 (l_j)​​ 最大的区间,设该区间的编号为 (d_i)

    于是我们先排序,再用双指针 (O(n))​​​ 求出所有区间 (i)​​​ 的 (d_i)​​

    接下来,对于必须包含区间 (i)​​​ 的询问,我们只需要不断跳 (d_i)​​​ 直到找到一个区间 (j)​​​ 满足 (r_j>l_i+m)​​​ 即可

    (+m)​​​ 是因为破环为链)

    我们发现这样暴力跳的时间 (O(n^2))​ 有点爆炸,考虑优化

    我们发现这样从 (i) 一步步向前跳的算法一般可以用倍增来优化(可以参考LCA)

    (d_{i,j}) 表示从 (i) 出发,跳 (2^j) 次到达的位置,预处理 (O(nlogn)) ,询问 (O(nlogn)),这道题就解决了。

    注意破环为链时有些小细节要注意,具体可以参考代码

    PS:据说有询问 (O(n))​ 的做法,就是先求出不强制包含区间的答案 (ans)​,若询问的区间在答案中使用了就输出 (ans) ,否则就输出 (ans+1)​​​

    代码

    #include<iostream>
    #include<algorithm>
    using namespace std;
    int n,m,ans[200005];
    int d[400005][20];
    struct node {
    	long long l,r;
    	int id;
    } a[400005];
    bool cmp(node a1,node a2) {
    	return a1.l<a2.l;
    }
    int main() {
    	scanf("%d%d",&n,&m);
    	for(int i=1; i<=n; i++) {
    		a[i].id=i;
    		scanf("%d%d",&a[i].l,&a[i].r);
    		if(a[i].l>a[i].r)a[i].r+=m;
    		a[i+n].l=a[i].l+m,a[i+n].r=a[i].r+m;
    	}
    	sort(a+1,a+1+n*2,cmp);
    	for(int i=1,j=1; i<=n*2; i++) {
    		while(j<n*2&&a[j+1].l<=a[i].r)j++;
    		d[i][0]=j;
    	}
    	for(int j=1; j<20; j++) {
    		for(int i=1; i<=n*2; i++) {
    			d[i][j]=d[d[i][j-1]][j-1];
    		}
    	}
    	for(int i=1; i<=n; i++) {
    		int x=i;
    		for(int j=19; j>=0; j--) {
    			if(a[d[x][j]].r<a[i].l+m) {
    				x=d[x][j];
    				ans[a[i].id]+=(1<<j);
    			}
    		}
    		ans[a[i].id]+=2;
    	}
    	for(int i=1; i<=n; i++)printf("%d ",ans[i]);
    	return 0;
    }
    

    Blackout

    题目思路

    发现 (N^2) 的值很大,显然不能将网格弄出来

    观察规则,发现如果将 ((x,y),(y,z))((z,x)) 看成有向边的话,好像能形成一个三元环

    我们考虑将网格看成有向图,将格子看成有向边,用三种颜色对这个有向图的所有弱连通分量按照 (0 ightarrow1,1 ightarrow2,2 ightarrow0) 的规则分别进行染色

    发现染色后三种情况

    • 无法进行染色(染色发生冲突)

      显然此时出现了二元环或自环,随便画画图发现此时该连通分量所有边都会被加上

      设点数为 (cnt_{node})​​ ,该连通分量的贡献即为 (cnt_{node}^2)​​

    • 成功染色

      • 没有颜色没被用过

      显然对于所有 (2 ightarrow0) 的边都会由 (0 ightarrow1,1 ightarrow2) 的边产生,(0 ightarrow1,1 ightarrow2) 的边同理

      设颜色为 (col) 的点数量为 (cnt_{col}),因此该连通分量的贡献即为 (cnt_0 imes cnt_1+cnt_1 imes cnt_2+cnt_2 imes cnt_0)

      • 有颜色没被用过

      显然除了该连通分量原本的边数外,不可能产生新的贡献,因此该连通分量的贡献即为其原本的边数

    代码

    #include<iostream>
    #include<cstdio>
    #include<vector>
    using namespace std;
    int n,m;
    struct edge {
    	int nxt,to,val;
    } e[200005];
    int tot,head[100005];
    void addedge(int u,int v,int w) {
    	e[++tot].to=v;
    	e[tot].val=w;
    	e[tot].nxt=head[u];
    	head[u]=tot;
    }
    bool flag;
    long long ecnt,ncnt,h[5];
    int c[100005];
    void dfs(int u,int col) {
    	ncnt++;
    	h[c[u]=col]++;
    	for(int i=head[u]; i; i=e[i].nxt) {
    		int v=e[i].to,w=e[i].val;
    		ecnt++;
    		int ncol=(w?col%3+1:(col+1)%3+1);
    		if(c[v]&&c[v]!=ncol)flag=false;
    		if(c[v])continue;
    		dfs(v,ncol);
    	}
    }
    int main() {
    	scanf("%d%d",&n,&m);
    	while(m--) {
    		int x,y;
    		scanf("%d%d",&x,&y);
    		addedge(x,y,1);
    		addedge(y,x,0);
    	}
    	long long ans=0;
    	for(int i=1; i<=n; i++) {
    		if(c[i])continue;
    		flag=true;
    		h[1]=h[2]=h[3]=0;
    		ncnt=ecnt=0;
    		dfs(i,1);
    		if(!flag)ans+=ncnt*ncnt;
    		else if(h[1]&&h[2]&&h[3])ans+=h[1]*h[2]+h[2]*h[3]+h[3]*h[1];
    		else ans+=ecnt/2;
    	}
    	printf("%lld",ans);
    	return 0;
    }
    

    Namori Grundy

    题目思路

    因为只有 (n)​ 个点 (n)​ 条边,而且每个点的入度显然都为 (0)​​ (因为 ((p_i,i))​ ​),所以很明显这个有向图一定是一个环发散出一些边

    我们暂时先不理会这个环,将这个环看作一个根,很明显这个有向图就是一棵外向树

    既然是外向树,那么叶子节点的权值显然为 (0) ,除根外其他结点的权值直接按模拟即可求出

    求出了除根外的其他结点,我们再来求这个根(环)

    思考后发现这个环上的每一个节点只会有两种取值,我们来证明一下

    设当前环上节点 (u)​​​​ 指向的所有节点的集合为 (S_u)​​​​,很明显 (S_u)​​​​ 中只有一个节点 (v)​​​​​ 是同样在环上的,(S_u)​ 中的其他节点均已求出

    我们再假设节点 (u)​ 指向的所有不在环上的节点的集合为 (s_u)​ ,那么 (u)​ 的一种可能取值 (a1_u)​ 即为 (mex {s_u})​,也就是点 (u)​ 忽略环上的点 (v)​ 直接求出的权值,令一种 (a_u)​ 可能的取值 (a2_u)​ 即为当 (a_v)​ 的取值为 (a1_u)​ 时 (mex {S_u})​,也就是点 (u)忽略环上的点 (v)​ 直接求出直接求出的权。可以发现当 (a_v)​ 的取值不为 (a1_u)​​ 时,(a_u) 的取值一定为 (a1_u)

    那么我们随便找一个点 (u) ,先暂时断开边 ((u,v)) ,将 (u) 的权值分别代入 (a1_u)(a2_u) 后判断与 (a_v) 的关系是否合法即可

    PS:我们发现,当且仅当这个环上所有点 (u)​ 的 (a1_u)​ 相等且为奇环时为 IMPOSSIBLE ,此时 (a_uin{a1_u,a1_u+1})​​ ,因此判断时代入权值 (a1_u)(a1_u+1)​ 即可​​

    代码

    (考试时写的,有点丑陋

    #include<iostream>
    #include<vector>
    #include<queue>
    #include<algorithm>
    using namespace std;
    int n,a[200005],d[200005];
    vector<int>g[200005],g2[200005];
    bool cmp(int g1,int g2) {
    	return a[g1]<a[g2];
    }
    queue<int>q;
    int get(int u) {
    	sort(g[u].begin(),g[u].end(),cmp);
    	if(g[u].size()==0||a[g[u][0]]!=0) {
    		return 0;
    	} else {
    		for(int i=0; i<g[u].size(); i++) {
    			if(i==g[u].size()-1||a[g[u][i+1]]-a[g[u][i]]>1) {
    				return a[g[u][i]]+1;
    			}
    		}
    	}
    }
    vector<int>bb;
    void run(int s) {
    	while(!q.empty()) {
    		int u=q.front();
    		q.pop();
    		for(int i=0; i<g2[u].size(); i++) {
    			int v=g2[u][i];
    			d[v]--;
    			if(s)bb.push_back(v);
    			if(d[v]==0)q.push(v);
    		}
    		if(u!=s)a[u]=get(u);
    	}
    }
    void back(){
    	for(int i=0;i<bb.size();i++){
    		d[bb[i]]++;
    	}
    }
    bool solve(int u) {
    	int v;
    	d[u]--;
    	for(int i=0; i<g[u].size(); i++) {
    		if(d[g[u][i]]) {
    			v=g[u][i];
    			g[u].erase(g[u].begin()+i);
    			break;
    		}
    	}
    	a[u]=get(u);
    	q.push(u);
    	run(u);
    	if(a[u]!=a[v])return true;
    	back();
    	a[u]=a[u]+1;
    	q.push(u);
    	run(u);
    	if(a[u]==a[v]+1)return true;
    	return false;
    }
    int main() {
    	scanf("%d",&n);
    	for(int i=1; i<=n; i++) {
    		int x;
    		scanf("%d",&x);
    		g[x].push_back(i);
    		g2[i].push_back(x);
    		d[x]++;
    	}
    	for(int i=1; i<=n; i++) {
    		if(d[i]==0)q.push(i);
    	}
    	run(0);
    	for(int i=1; i<=n; i++) {
    		if(d[i]) {
    			if(solve(i))printf("POSSIBLE");
    			else printf("IMPOSSIBLE");
    			return 0;
    		}
    	}
    	printf("POSSIBLE");
    	return 0;
    }
    
  • 相关阅读:
    spring中bean的高级属性之list, set, map以及props元素(含举例)
    Schema约束与DTD约束
    spring中配置Properties对象的方法
    web.xml 中的listener、 filter、servlet 加载顺序及其详解
    MySql为某个表增加rownumber
    分组取前N记录(转)
    1、Spring MVC的web.xml配置详解(转)
    Spring的PropertyPlaceholderConfigurer应用(转)
    几种任务调度的 Java 实现方法与比较(定时任务)(转)
    Spring定时任务的几种实现(转)
  • 原文地址:https://www.cnblogs.com/ezlmr/p/15090733.html
Copyright © 2020-2023  润新知