zoukankan      html  css  js  c++  java
  • Mail.Ru Cup 2018 Round 2

    Mail.Ru Cup 2018 Round 2


    C. Lucky Days

    题意:找出最长的一段连续区间,同时被([l_a + k_at_a, r_a + k_at_a]) , ([l_b + k_bt_b, r_b + k_bt_b])覆盖。

    做法:设最终的答案为([L,R]),那么(L)一定是(l_a + k_at_a,~~ l_b + k_bt_b), (R)同理。根据不同条件,解不等式,然后判断是否有解即可。其中(k_ata - k_bt_b = k* gcd(t_a,t_b))

    #include <bits/stdc++.h>
    #define VI vector<int>
    #define VL vector<ll> 
    #define P pair<ll,int>
    #define fr first
    #define sc second
    #define pb push_back
    #define rep(i,a,b) for(int i = a; i <= b; ++i) 
    #define per(i,a,b) for(int i = a; i >= b; --i) 
    #define mem(a,b) memset(a,b,sizeof(b))
    typedef long long ll;
    const ll inf = 1000000000000000000LL;
    using namespace std;
    ll ra,la,ta,rb,lb,tb,ans;
    ll fd(ll x,ll g) {
        ll t;
        t = x/g, t*=g;
        while(t < x) t+=g;
        return t;
    }
    int main() {
    	scanf("%lld%lld%lld",&la,&ra,&ta);
    	scanf("%lld%lld%lld",&lb,&rb,&tb);
    	ll g = __gcd(ta,tb);
    	if(ra-rb <= la - lb && fd(ra-rb,g) <= la-lb ) {
    		ans = max(ra-la+1,ans);
    		printf("%lld
    ",ans); return 0;
    	}
    	if(rb-ra <= lb - la && fd(rb-ra,g) <= lb-la ) {
    		ans = max(rb-lb+1,ans);
    		printf("%lld
    ",ans); return 0;
    	}
        ll mx = max(la-lb,la-rb);
    	//cout << mx << ' '<< ra-rb << endl;
    	if(fd(mx,g) <= (ra-lb)) {
        	ll X = fd(mx,g);
    //		cout << "ff" << endl;
           	ans = max((ra-lb+1) - X,ans);
    	}
        mx = max(lb-la,lb-ra);
        if(fd(mx,g) <= (rb-la)) {
            ll X = fd(mx,g);
    //	cout << "f";
            ans = max((rb-la+1) - X,ans);
        }
        printf("%lld
    ",ans);
    	return 0;
    }
    

    D. Refactoring

    题意:对第一组的每个串,第一次出现的串s,将它变成t,可以获得第二组串,现在给定两组串求s和t。

    做法:对每个串都取确定了一些转换,先找到每个串第一个转换关系,然后尽量向两边扩展这个串,然后就可以确定一个s,再对每个第一组串跑kmp验证即可。慎用strstr()...

    #include <bits/stdc++.h>
    typedef long long ll;
    const int N = 3003;
    using namespace std;
    int n, l[N], st[N], ed[N], idx, cc;
    char s1[N][N], s2[N][N], str[N];
    int ck0() {
    	for(int i = 1; i <= n; ++i) if(st[i] != 0) {
    		if(s1[i][st[i]] == s1[idx][st[idx]] && s2[i][st[i]] == s2[idx][st[idx]] ) continue;
    		else return 0;
    	}
    	return 1;
    }
    int ck1() {
    	for(int i = 1; i <= n; ++i) if(st[i] != 0) {
    		if(st[i] == 1) return 0;
    		else {
    			if(s1[i][st[i]-1] == s1[idx][st[idx]-1] && s2[i][st[i]-1] == s2[idx][st[idx]-1] ) continue;
    			else return 0;
    		}
    	}
    	return 1;
    }
    int ck2() {
    	for(int i = 1; i <= n; ++i) if(st[i] != 0) {
    		if(ed[i] == l[i]) return 0;
    		else {
    			if(s1[i][ed[i]+1] == s1[idx][ed[idx]+1] && s2[i][ed[i]+1] == s2[idx][ed[idx]+1] ) continue;
    			else return 0;
    		}
    	}
    	return 1;
    }
    
    int nxt[N];
    void getnxt(char s[],int n){
        nxt[1]=0;
        for(int i=2;i<=n;i++){
            int j=nxt[i-1];
            while(j&&s[j+1]!=s[i]) j=nxt[j];
            if(s[j+1]==s[i]) nxt[i]=j+1;
            else nxt[i] = 0;
        }
    }
    int kmp(char p[],int m, char s[], int n){
        int j=0;
        for(int i=1;i<=n;i++){
            while(j&&p[j+1]!=s[i]) j=nxt[j];
            if(p[j+1]==s[i])++j;
            if(j==m) {        
            	return i-m+1;
            }
        }
        return -1;
    }
    int main() {
    	scanf("%d",&n);
    	for(int i = 1; i <= n; ++i) scanf(" %s",s1[i]+1), l[i] = strlen(s1[i]+1);
    	for(int i = 1; i <= n; ++i) scanf(" %s",s2[i]+1);
    	for(int i = 1; i <= n; ++i) {
    		for(int j = 1; j <= l[i]; ++j) if(s1[i][j] != s2[i][j]) {
    			idx = i;
    			st[i] = ed[i] = j;
    			break;
    		}
    	}
    	if(idx == 0) {
    		puts("YES"); printf("a
    a
    ");
    		return 0;
    	}
    	if(!ck0()) {
    		return  puts("NO"),0;
    	}
    	while(ck1()) {
    		for(int i = 1; i <= n; ++i)if(st[i]!=0) --st[i];
    	}
    	while(ck2()) {
    		for(int i = 1; i <= n; ++i)if(ed[i]!=0) ++ed[i];
    	}
    	for(int i = st[idx]; i <= ed[idx]; ++i) str[++cc] = s1[idx][i]; str[cc+1] = '';
    	
    	getnxt(str,cc);
    	for(int i = 1; i <= n; ++i) {
    		int p = kmp(str,cc,s1[i],l[i]);
    		if(st[i] != 0) {
    			if(p != st[i]) return puts("NO"),0;
    		}
    		else if(p != -1) {
    			return puts("NO"),0;
    		}
    	}
    	puts("YES");
    	printf("%s
    ",str+1);
    	for(int i = st[idx]; i <= ed[idx]; ++i) printf("%c",s2[idx][i]);
    	puts("");
    	return 0;
    }
    

    E. Segments on the Line

    题意:给定一个长度为(n)的序列(a),以及(s)个区间([l_i,r_i]),从这些区间中,挑恰好(m)个区间,他们的并区间中元素第(k)小的值为这种方案的答案,求最小的答案。

    做法:首先,二分第(k)小的值(v),然后问题就转换为求,挑(m)个区间他们中小于(v)的数最多是多少。将区间按照右端点排序,(dp[i][j])表示前(i)个区间选了(j)个,且第(i)个一定选了的小于(v)的数目,转移有两种:1)第(i)个区间与前一个区间不相交,这个需要维护每个位置向前,用了(j)个区间,的(dp)的最大值;2)第(i)个区间与前一个区间不相交,这时取上一个区间取左端点尽可能小的一定更优,因为较大的左端点一定可以被他包含,对每个区间维护一下前一个不相交的区间,和相交的中左端点最小的区间。还有一种,情况有区间相互包含的情况,显然取大的更优。(我的实现有点麻烦。。懒得改了。

    #include <bits/stdc++.h>
    #define pb push_back
    typedef long long ll;
    const int N = 1600;
    using namespace std;
    int n,s,m,k,a[N];
    vector<int> v[N];
    struct node{ int l,r; } b[N];
    bool cmp(node a,node b) {
    	if(a.r != b.r) return a.r < b.r;
    	return a.l < b.l;
    }
    int dp[N][N], MX[N][N], pre[N], pl[N];
    int cal(int x) {
    	int ans = 0;
    	memset(dp,0,sizeof(dp));
    	dp[1][1] = MX[1][1] = upper_bound(v[1].begin(),v[1].end(),x) - v[1].begin();
    	for(int i = 1,c=0,c2=0; i <= s; ++i) {
    		c = c2 = 0;
    		if(pl[i] != 0) {
    			for(int j = b[pl[i]].r+1; j <= b[i].r; ++j) c2 += (a[j] <= x);
    		}
    		c = upper_bound(v[i].begin(),v[i].end(),x) - v[i].begin();
    		dp[i][1] = c;
    		for(int j = 2; j <= i && j <= m; ++j) {
    			if(pre[i] != 0) dp[i][j] = max(dp[i][j], MX[pre[i]][j-1] + c);
    			if(pl[i] != 0) dp[i][j] = max(dp[i][j], dp[pl[i]][j-1] + c2);
    			dp[i][j] = max(dp[i][j], dp[i][j-1]);
    		}
    		for(int j = 1; j <= i && j <= m; ++j) 
    			MX[i][j] = max(MX[i-1][j], dp[i][j]);
    		ans = max(ans, dp[i][m]);
    	}
    	return ans;
    }
    
    int main() {
    	scanf("%d%d%d%d",&n,&s,&m,&k);
    	for(int i = 1; i <= n; ++i) scanf("%d",&a[i]);
    	for(int i = 1; i <= s; ++i) {
    		scanf("%d%d",&b[i].l,&b[i].r);
    	}
    	sort(b+1, b+1+s, cmp);
    	for(int i = 1; i <= s; ++i) { 
    		for(int j = b[i].l ; j <= b[i].r ; ++j) v[i].pb(a[j]);
    		sort(v[i].begin(),v[i].end());
    	}
    	for(int i = 1; i <= s; ++i) {
    		int mx = 0, idx = 0, mn = 1000000, idx2 = 0;
    		for(int j = 1; j < i; ++j) {
    			if(b[j].r < b[i].l && mx < b[j].r) {
    				mx = b[j].r; idx = j;
    			}
    			if(b[j].r >= b[i].l && mn > b[j].l) {
    				mn = b[j].l; idx2 = j;
    			}
    		}
    		pre[i] = idx; pl[i] = idx2;
    	}
    	int l = 1, r = 1e9, mid, ans = -1;
    	while(l <= r) {
    		ll mid = (l + r) >> 1;
    		if(cal(mid) >= k) r = mid-1, ans = mid;
    		else l = mid + 1;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    F. Tree and XOR

    题意:给定一棵(n)节点的树,每条边都有权值,一条从(u)(v)的路径的价值定义为路径上边权的异或和,问所有(n^2)个路径中,第(k)小的路径的异或和是多少。

    做法:两点之间路径的价值,可以通过他们到根的前缀异或和相异或求得。于是,问题转化为(n)个数,问他们两两之间的(n^2)个异或值中,第(k)小的是什么。首先,一个思路就是,二分答案,然后枚举一个数,在(trie)树上查询比这个数小的数的个数。复杂度是有两个(log)。考虑直接通过(trie)树,确定答案的每一位是什么,对于每个数维护他当前所在的(Trie)树上的节点,计算当前层异或为(0)的数目(sum),如果(k>sum),那么这一位是(1),否则是(0)。然后,更新每个数,在(Trie)树上的位置。这样时间可以做到(O(62logn)),但是,空间依然不够,看了别人的代码,可以换一种实现,在建(Trie)树的同时,计算,每次先插入当前层的数,建好当前层,再枚举计算当前位异或为(0)的数目,更新每个数的位置。注意到我们每次使用的节点只有上一层的,那么就可以每次只保留最后一层的节点,新的节点可以复用之前的空间

    #include <bits/stdc++.h>
    typedef long long ll;
    const int N = 1000100;
    using namespace std;
    int n;
    ll k,w[N],ans;
    int rt[N], prt[N], num[N], cc, ch[N][2];
    int main() {
        scanf("%d%lld",&n,&k);
        for(int p,i = 2; i <= n; ++i) scanf("%d%lld",&p,&w[i]), w[i]^=w[p];
        for(int i = 1; i <= n; ++i) rt[i] = prt[i] = 1;
        for(int s = 61; s >= 0; --s) {
            for(int i = 1; i <= cc; ++i) ch[i][0]=ch[i][1]=num[i]=0; cc = 1;
    
            for(int i = 1; i <= n; ++i) {
                int t = (w[i]>>s)&1;
                if(!ch[rt[i]][t]) ch[rt[i]][t] = ++cc;
                rt[i] = ch[rt[i]][t];
                num[rt[i]]++;
            }
            ll sum = 0;
            for(int i = 1; i <= n; ++i) {
                int t = (w[i]>>s)&1;
                sum += num[ch[prt[i]][t]];
            }
            if(sum < k) {
                ans |= (1LL<<s);
                k-=sum;
                for(int i = 1; i <= n; ++i) {
                    int t = (w[i]>>s)&1;
                    prt[i] = ch[prt[i]][t^1];
                }
            }
            else {
                for(int i = 1; i <= n; ++i) {
                    int t = (w[i]>>s)&1;
                    prt[i] = ch[prt[i]][t];
                }
            }
        }
        printf("%lld
    ",ans);
        return 0;
    }
    
    
  • 相关阅读:
    java实现RSA非对称加密
    lombok中的@Builder注解
    java实现大文件的分割与合并
    IDEA新建springboot选择DevTools
    bat命令自动配置java环境变量
    java实现发送邮件
    随记
    编译原理学习——FIRST和LASTVT
    国王的游戏
    JAVA类加载及NEW对象的过程
  • 原文地址:https://www.cnblogs.com/RRRR-wys/p/9969522.html
Copyright © 2011-2022 走看看