zoukankan      html  css  js  c++  java
  • Codeforces Round #542 div1

    A

    同一起点如果有多个任务的话 最后走最短的那个 有几个走几次
    然后把每个起点的贡献取一个max即可
    在“会有跨越起点的情况”那里自闭了好久。。
    但实际上每个点如果只走一次的话贡献的最远距离就是起点到它的距离+它要走的距离

    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    #include <vector>
    #include <set>
    #define sub(x, y) (x >= y ? x - y : x + n - y)
    using namespace std;
    const int N = 1e5 + 5;
    int n, m;
    int len[N];
    long long cnt[N];
    int main(){ 
        scanf("%d%d", &n, &m);
        memset(len, 0x3f, sizeof(len));
        for(int i = 1, x, y; i <= m; ++i){
        	scanf("%d%d", &x, &y);
        	++cnt[x]; len[x] = min(len[x], sub(y, x));
        }
        for(int i = 1; i <= n; ++i) if(cnt[i]) cnt[i] = (cnt[i] - 1) * n; else cnt[i] = -1;
        
        for(int i = 1; i <= n; ++i){
        	long long ans = 0;
    		for(int j = 1; j <= n; ++j) if(~cnt[j])
    			ans = max(ans, cnt[j] + len[j] + sub(j, i));
    		printf("%lld ", ans);
        }
    	return 0;
    }
    

    B

    怀疑这是假的。。
    钦定数组长度为2000 设后面1999项的和为x
    那么2000(x - 1) - 1999x = k
    解得x = k

    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    #include <vector>
    #include <set>
    #define sub(x, y) (x >= y ? x - y : x + n - y)
    using namespace std;
    const int N = 1e6;
    int main(){ 
        int k; scanf("%d", &k); k += 2000;
        printf("2000
    -1 ");
        for(int i = 2; i <= 2000; ++i) printf("%d ", k > N ? N : k), k -= (k > N ? N : k);
    	return 0;
    }
    

    C

    3000可以资瓷平方
    求前缀的所有后缀嘛 那就用倒序的前缀字典树
    这样每个点表示一个前缀的后缀

    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    #include <vector>
    #include <set>
    using namespace std;
    const int N = 3005;
    const long long P = 1e9 + 7;
    int n, sz, a[N], ch[N * N][2], fa[N * N];
    long long f[N * N], ans;
    inline void add(long long &x, long long y){x += y; if(x >= P) x -= P;}
    int main(){
    	scanf("%d", &n); sz = 1, f[1] = 1;
     	for(int i = 1; i <= n; ++i){
    		scanf("%d", &a[i]);
    		for(int j = i, cur = 1; j >= 1; cur = ch[cur][a[j]], --j){
    			if(ch[cur][a[j]]) continue;
    			++sz;
    			ch[cur][a[j]] = sz; fa[sz] = cur;
    			for(int k = 0, vv = cur, st = 0; k <= 3 && vv; ++k, vv = fa[vv]){
    				st = (st << 1) | a[j + k];
    				if(k < 3 || (st != 3 && st != 5 && st != 14 && st != 15)) add(f[sz], f[vv]);
    			}
    			add(ans, f[sz]);
    		}
    		printf("%lld
    ", ans);
    	}
    	return 0;
    }
    

    D

    来自zsy
    description
    求将{ai}分成若干段使每一段内都有恰好k个数出现了恰好一次的方案数模998244353。

    solution
    右端点从左往右扫,维护prei表示i前面最近的一个与ai相等数的位置(没有则为0),
    每次将[prei+1,i]这段区间+1,将[preprei+1,prei]这段区间−1(如果prei≠0的话),
    然后只要查前缀所有恰好等于k的位置的dp值之和就行了。
    关于维护答案有一个点要注意 就是每次区间都是加一减一
    所以每次整块也只会减去一个权值的贡献或加上一个权值的贡献[见*处]

    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    #include <vector>
    #include <map>
    using namespace std;
    const int N = 1e5 + 5;
    const int P = 998244353;
    int n, m, a[N], pre[N], rec[N], f[N];
    inline void add(int &x, int y){
    	x += y; if(x >= P) x -= P; 
    }
    inline void sub(int &x, int y){
    	x -= y; if(x < 0) x += P;
    }
    struct BL{
        int bel[N], tag[450], w[N], size, cnt, sum[450][N], ans[450]; 
        void init(){
        	sum[1][0] = ans[1] = 1;
    		size = sqrt(n);
        	for(int i = 1; i <= n; ++i) bel[i] = (i - 1) / size + 1;
        	cnt = bel[n];
        	for(int i = 1; i <= cnt; ++i) tag[i] = m;
        }
        void update(int pos, int x){
        	int px = bel[pos];
        	sub(sum[px][w[pos]], f[pos - 1]); 
        	if(w[pos] <= tag[px]) sub(ans[px], f[pos - 1]);
        	w[pos] = x;
        	add(sum[px][x], f[pos - 1]); 
    		if(x <= tag[px]) add(ans[px], f[pos - 1]);
        }
        void ins(int x, int y, int d){
        	if(x > y) return;
        	if(bel[x] + 1 >= bel[y]){
        		for(int i = x; i <= y; ++i) update(i, w[i] + d);
        		return ;
        	}
            for(int i = x; i <= bel[x] * size; ++i) update(i, w[i] + d);
            for(int i = (bel[y] - 1) * size + 1; i <= y; ++i) update(i, w[i] + d);
            for(int i = bel[x] + 1; i <= bel[y] - 1; ++i){
            	if(d > 0) sub(ans[i], sum[i][tag[i]]);
    			tag[i] -= d;
    			if(d < 0) add(ans[i], sum[i][tag[i]]);//*
            } 
        }
        int qry(int x){
        	int res = 0;
        	for(int i = x; bel[i] == bel[x]; --i) if(w[i] <= tag[bel[x]]) add(res, f[i - 1]);
            for(int i = bel[x] - 1; i >= 1; --i) add(res, ans[i]);
            return res;
    	}
    }bl;
    int main(){
    	scanf("%d%d", &n, &m); bl.init();
    	f[0] = 1;
    	for(int i = 1; i <= n; ++i){
    		scanf("%d", &a[i]);
    		pre[i] = rec[a[i]], rec[a[i]] = i;
    		bl.ins(pre[pre[i]] + 1, pre[i], -1), bl.ins(pre[i] + 1, i, 1);
    		f[i] = bl.qry(i);
    		add(bl.sum[bl.bel[i + 1]][0], f[i]);
    		add(bl.ans[bl.bel[i + 1]], f[i]);//记得这里要把下一位出现0次的贡献插入
    	} 
    	printf("%d
    ", f[n]);
    	return 0;
    }
    

    E

    有一颗n个节点的树
    每次可以询问两个点集S,T和一个点v,
    询问有多少对((s, t) (s∈S, t∈T))
    使得v在(s,t)的路径上。
    要求还原出这棵树的形态。
    (n≤500,Q leq 11111)

    每次询问都变成1到一个点集 这样询问的就是到根路径
    最初询问(S = {1}, T = {2, 3, ..., n}, v = 2, 3, ..., n)
    这样得到每个点的size
    按照size升序排列 删掉新加入点在已有集合的所有儿子
    然后把它加到集合里

    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    #include <vector>
    #include <set>
    #define mp(x, y) make_pair(x, y)
    using namespace std;
    typedef pair<int, int> PII;
    const int N = 505; 
    int n, m, id[N], size[N];
    vector<int> vec, tot;
    vector<PII> edge;
    inline int query(vector<int> S, int x){
    	if(S.empty()) return 0;
    	printf("1
    1
    %d
    ", S.size());
    	for(int i : S) printf("%d ", i);
    	printf("
    %d
    ", x); fflush(stdout);
    	scanf("%d", &x); return x;
    }
    inline bool rule(int x, int y){
    	return size[x] < size[y]; 
    }
    int main(){
    	scanf("%d", &n); size[1] = n, id[1] = 1;
    	for(int i = 2; i <= n; ++i) tot.push_back(i), id[i] = i;
    	for(int i = 2; i <= n; ++i) size[i] = query(tot, i);
    	sort(id + 2, id + n + 1, rule);
    	for(int ii = 2, i = id[ii]; ii <= n; ++ii, i = id[ii]){
    		int j = query(vec, i);
    		while(j--){
    			int l = 0, r = vec.size() - 1, mid; vector<int> tmp;
    			while(l < r){
    				mid = l + ((r - l) >> 1);
    				tmp.clear(); for(int k = 0; k <= mid; ++k) tmp.push_back(vec[k]);
    				if(query(tmp, i)) r = mid;
    				else l = mid + 1;
    			}
    			edge.push_back(mp(i, vec[l]));
    			vec.erase(vec.begin() + l);//注意这里删除元素的方式 
    		}
    		vec.push_back(i);
    	}
    	for(int i : vec) edge.push_back(mp(1, i));
    	printf("ANSWER
    ");
    	for(PII i : edge) printf("%d %d
    ", i.first, i.second);
    	return 0;
    }
    /*
    5
    4
    2
    1
    1
    0
    1
    1
    2
    1
    
    */
    
  • 相关阅读:
    MySQL数据库的优化
    PHP中获取文件扩展名
    PHP实现几种经典算法详解
    Linux服务器上crontab定时执行脚本文件
    LeetCode每日一题(五):加一
    巧妙利用枚举找出数组元素所在区间
    PHP实现几种经典算法详解
    _initialize() 区别 __construct()
    LeetCode每日一题(四):搜索插入位置
    LeetCode每日一题(三):移除元素
  • 原文地址:https://www.cnblogs.com/hjmmm/p/10798030.html
Copyright © 2011-2022 走看看