zoukankan      html  css  js  c++  java
  • Codeforces Global Round 15

    E. Colors and Intervals

    题目描述

    点此看题

    (n) 种颜色,每种颜色恰好有 (k) 个,他们排成一个长度为 (n imes k) 的颜色序列 (a)

    每种颜色需要选两个端点,这两个点会构成一个区间,试构造方案使得每个点最多被覆盖 (lceilfrac{n}{k-1} ceil) 次。

    (n,kleq 100)

    解法

    不难转化题意:每 (k-1) 个颜色分成一组,要求每一组的区间最多覆盖每个点一次。

    再观察每种颜色 (k) 个端点,那么 (k-2) 个端点会被浪费,可以猜想每种颜色的匹配最多浪费其他颜色的一个端点。

    我们取出这一组所有有关的端点,然后从左往右扫,如果这种颜色没有左端点那么记录左端点,如果这种颜色有左端点那么把这个位置当成右端点,然后把其它颜色的左端点往右移直接不交,不难发现其他颜色最多移动一次,因为如果移动多次那么先匹配的就是它了。

    总结

    和次数相关的构造题一定要找次数的现实意义,除法可以联想到分组,当然如果题目给的是具体数字我们要猜这个数字是怎么来的。

    其实我觉得第二步构造是很难的,但是从感觉上说我们想把距离近的点匹配到一起,所以本题的顺序扫描法是很有可能的,我一开始想的时候想的是归纳构造法但是走不通,有时候整体和局部的构造方法都值得去想一想。

    #include <cstdio>
    #include <vector>
    #include <iostream>
    using namespace std;
    const int M = 100005;
    #define pii pair<int,int>
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,k,a[M],b[M],nxt[M];pii ans[M];
    signed main()
    {
    	n=read();k=read();
    	for(int i=1;i<=n*k;i++)
    		a[i]=read();
    	for(int l=1;l<=n;l+=k-1)
    	{
    		int r=min(n,l+k-2);vector<int> g;
    		for(int i=1;i<=n*k;i++)
    			if(l<=a[i] && a[i]<=r)
    				g.push_back(i);
    		for(int i=0;i<g.size();i++)
    		{
    			if(b[a[g[i]]]) nxt[b[a[g[i]]]]=g[i];
    			b[a[g[i]]]=g[i];
    		}
    		for(int i=l;i<=r;i++) b[i]=0;
    		for(int i=0;i<g.size();i++)
    		{
    			if(!ans[a[g[i]]].first && b[a[g[i]]] && b[a[g[i]]]!=g[i])
    			{
    				ans[a[g[i]]]=make_pair(b[a[g[i]]],g[i]);
    				for(int j=l;j<=r;j++)
    					if(b[j]<=g[i]) b[j]=nxt[b[j]];
    			}
    			else b[a[g[i]]]=g[i];
    		}
    	}
    	for(int i=1;i<=n;i++)
    		printf("%d %d
    ",ans[i].first,ans[i].second);
    }
    

    G. A Serious Referee

    题目描述

    点此看题

    哥哥 ( t zxy) 设计了一种排序方法,这个排序操作有 (k) 步,每次选择一个子序列将其内部升序排列。

    天下第一妹的 ( t jzm) 要审核这个排序方法,所以她想知道这种排序方法适用于所有长度为 (n) 的序列。

    (nleq 40,kleq 10)

    解法

    ( t zero-one principle):对于任何的排序方式,长度为 (n) 的序列能排好序当且仅当长度为 (n)(0/1) 序列能排好序,必要性显然,下证充分性:

    不妨设原序列为排列,对于所有 (1leq sleq n),我们把小于等于 (s) 的元素设置为 (0),大于 (s) 的元素设置为 (1),然后把这个 (0/1) 序列排序,我们可以得到下列信息:

    • 排序后小于等于 (s) 的元素在前 (s) 个位置上。

    那么可以依次推出最小的元素在第一个位置,次小的元素在第二个位置(...)充分性得证

    那么我们只需要考虑对所有 (01) 序列是否能排好序即可,暴力枚举 (O(2^{40})) 受不了,而折半搜索又不行。

    现在排序之后状态量会减少,那么我们按操作来考虑,考虑到第 (i) 个操作时只记录前 (i) 个操作涉及到的位置的状态。我们考虑怎么扩展状态,每次我们考虑新增的位置的状态,但是因为要排序我们只需要枚举新增的位置有多少个 (1) 就可以得到新的状态了。

    因为新增位置的总数是 (n),而最多新增 (k) 次,设每次新增了 (s_i) 个:

    [(s_1+1)(s_2+1)...(s_k+1)leq (frac{n+k}{k})^k=O(5^k) ]

    然后就可以开始卡常了,判断一个 (01) 序列是否排好序的方法:找出 (0) 的个数,然后看前这么多位是否全是 (0) 即可。因为这道题范围是 ( t ll),你要用 __builtin_popcountll(x),这是针对 ( t ll) 统计二进制有多少个 (1) 的函数。

    总结

    像这种有很奇怪数据范围的题,一定注意减少状态量即可,思考按什么顺序考虑状态会少一些。

    #pragma GCC optimize(2)
    #include <cstdio>
    #include <cassert>
    #include <algorithm>
    using namespace std;
    const int M = 105;
    const int N = 10000005;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,k,b[M],x[M],w[N],h[N],st[M];
    signed main()
    {
    	n=read();k=read();w[m=1]=0;
    	for(int i=1;i<=n;i++)
    		st[i]=st[i-1]|(1ll<<i-1);
    	for(int i=1;i<=k;i++)
    	{
    		int p=read();
    		for(int j=1;j<=p;j++)
    		{
    			x[j]=read()-1;
    			b[i]|=1ll<<x[j];
    		}
    		int s=b[i-1]&b[i],t=m;m=0;
    		int sz=__builtin_popcountll(b[i]-s);
    		for(int j=1;j<=t;j++) h[j]=w[j];
    		for(int j=1;j<=t;j++)
    		{
    			int cnt=__builtin_popcountll(s&h[j]);
    			int st=h[j]^(s&h[j]);
    			//printf("%d %d
    ",cnt,sz);
    			for(int k=0;k<cnt;k++)
    				st|=(1ll<<x[p-k]);
    			for(int k=cnt;k<=sz+cnt;k++)
    				w[++m]=st,st|=(1ll<<x[p-k]);
    		}
    		b[i]|=b[i-1];
    	}
    	if(n==1)
    	{
    		puts("ACCEPTED");
    		return 0;
    	}
    	if(b[k]!=(1ll<<n)-1)
    	{
    		puts("REJECTED");
    		return 0;
    	}
    	for(int i=1;i<=m;i++)
    	{
    		int sz=n-__builtin_popcountll(w[i]);
    		if(w[i]&st[sz])
    		{
    			puts("REJECTED");
    			return 0;
    		}
    	}
    	puts("ACCEPTED");
    }
    
  • 相关阅读:
    java中sleep()和wait()区别
    那些年遇到的坑--------“集合转数组”
    HashMap中推荐使用entrySet方式遍历Map类集合KV而不是keySet方式遍历
    java.util.ConcurrentModificationException 异常原因和解决方法
    java.lang.Exception: No tests found matching
    https和http的主要区别
    交换性别sql
    判断奇偶数
    Jmeter安装使用
    java.io.EOFException: Unexpected EOF read on the socket
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15156174.html
Copyright © 2011-2022 走看看