zoukankan      html  css  js  c++  java
  • 2017-2018 Petrozavodsk Winter Training Camp, Saratov SU Contest 部分题解

    2017-2018 Petrozavodsk Winter Training Camp, Saratov SU Contest 部分题解

    A.Three Arrays

    题意

    给定三个长度分别为(n_a,n_b,n_c)的有序数组(a,b,c)

    计算三元组个数((i,j,k))使得(|a_i - b_j| leq d,|a_i - c_k| leq d,|b_j - c_k|leq d)

    分析

    可以发现这个条件是比较强的,直接算不好算。我们钦定最小值,即枚举每个元素作为最小值的方案数

    注意到会有重复计算,如同样枚举1作为最小值,可能有(1,1,1)被算多次。

    去重方法就是下一次计算不要算大于等于,算大于就行了

    代码

    #include<bits/stdc++.h>
    #define re register
    using namespace std;
    
    typedef long long ll;
    
    inline int rd(){
    	int x = 0;
    	int f = 1;
    	char ch = getchar();
    	while(ch < '0' || ch > '9') {
    		if(ch == '-') f = -1;
    		ch = getchar();
    	}
    	while(ch >= '0' && ch <= '9') {
    		x = x * 10 + ch - '0';
    		ch = getchar();
    	}
    	return x * f;
    }
    
    int main(){
    	int d;
    	while(~scanf("%d",&d)){
    		int na = rd();
    		int nb = rd();
    		int nc = rd();
    		vector<int> v1(na),v2(nb),v3(nc);
    		unordered_map<int,int> mp1,mp2,mp3;
    		for(int i = 0;i < na;i++)
    			v1[i] = rd(),mp1[v1[i]]++;
    		for(int i = 0;i < nb;i++)
    			v2[i] = rd(),mp2[v2[i]]++;
    		for(int i = 0;i < nc;i++)
    			v3[i] = rd(),mp3[v3[i]]++;
    		ll ans = 0;
    		for(int i = 0;i < na;i++){
    			int cnt1 = upper_bound(v2.begin(),v2.end(),v1[i] + d) - lower_bound(v2.begin(),v2.end(),v1[i]);
    		   	int cnt2 = upper_bound(v3.begin(),v3.end(),v1[i] + d) - lower_bound(v3.begin(),v3.end(),v1[i]);	
    			ans += (ll)cnt1 * cnt2;
    		}
    		for(int i = 0;i < nb;i++){
    			int cnt1 = upper_bound(v1.begin(),v1.end(),v2[i] + d) - upper_bound(v1.begin(),v1.end(),v2[i]);
    		   	int cnt2 = upper_bound(v3.begin(),v3.end(),v2[i] + d) - lower_bound(v3.begin(),v3.end(),v2[i]);	
    			ans += (ll)cnt1 * cnt2;
    		}
    		for(int i = 0;i < nc;i++){
    			int cnt1 = upper_bound(v1.begin(),v1.end(),v3[i] + d) - upper_bound(v1.begin(),v1.end(),v3[i]);
    			int cnt2 = upper_bound(v2.begin(),v2.end(),v3[i] + d) - upper_bound(v2.begin(),v2.end(),v3[i]);
    			ans += (ll)cnt1 * cnt2;
    		}
    		printf("%lld
    ",ans);
    	}
    }
    

    C. Cover the Paths

    代码

    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int mod=1e9+7;
    const int maxn=1e5+10;
    
    int n,m;
    vector<int>g[maxn];
    int vis[maxn];
    vector<int>ans;
    map<pair<int,int>,int>mp;
    set<int>st[maxn];
    
    void dfs(int u,int fa)
    {
        for(auto v:g[u]){
            if(v==fa)continue;
            dfs(v,u);
    
            if(st[u].size()>st[v].size()){
                for(auto now:st[v]){
                    auto pos=st[u].find(now);
                    if(pos!=st[u].end()){
                        vis[u]=1;
                    }else{
                        st[u].insert(now);
                    }
                }
            }else{
                for(auto now:st[u]){
                    auto pos=st[v].find(now);
                    if(pos!=st[v].end()){
                        vis[u]=1;
                    }else{
                        st[v].insert(now);
                    }
                }
                swap(st[u],st[v]);
            }
        }
    
        if(vis[u]){
            ans.push_back(u);
            st[u].clear();
        }
    }
    int main()
    {
        cin>>n;
        for(int i=1;i<n;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        cin>>m;
        for(int i=1;i<=m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            if(x>y)swap(x,y);
            if(x==y){
                //ans.push_back(x);
                vis[x]=1;
            }
            if(mp[{x,y}]==1)continue;
            st[x].insert(i);
            st[y].insert(i);
            mp[{x,y}]=1;
        }
        dfs(1,0);
        cout<<ans.size()<<endl;
        for(auto now:ans){
            printf("%d ",now);
        }
    }
    

    D. Elevator

    题意

    (n)个人从0层开始坐电梯到指定层数,电梯的容量是无限的。

    每个人有到达时间和指定层,电梯移动一层花费1单位时间。问把所有人送到指定层并回到0层的总时间是多少。

    分析

    首先可以对问题简化。我们只需要得到非递增的指定层序列,如 2 5 3 4 2 3。

    完全可以简化为5 4 3,容易证明这样的策略不会更劣。

    然后可以考虑枚举上次一次上典题的位置,容易得出转移方程(dp_i = min_j{max{dp_j + 2h_{i+1},t_i + 2h_{i+1}}})

    取max是因为必须要等到那个时刻典题到达0层,且第i个人到达。

    这题很好的性质在于dp和t都是单调的,于是可以用类似双指针的方法再配合堆来优化复杂度 ,具体细节不做解释

    代码

    #include<bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<ll,ll>
    #define re register
    
    using namespace std;
    
    typedef long long ll;
    
    ll rd(){
    	ll x = 0;
    	char ch = getchar();
    	while(ch < '0' || ch > '9') {
    		ch = getchar();
    	}
    	while(ch >= '0' && ch <= '9'){
    		x = x * 10 + ch - '0';
    		ch = getchar();
    	}
    	return x;
    
    }
    
    const int maxn = 2e5 + 5;
    
    int main(){
    	int n;
    	while(~scanf("%d",&n)){
    	vector<int> t,a;
    	vector<ll> dp(n + 1);
    	for(int i = 1;i <= n;i++){
    		int tt = rd();
    		int aa = rd();
    		while(!a.empty() && aa >= a.back()) a.pop_back(),t.pop_back();
    		a.push_back(aa);
    		t.push_back(tt);
    	}
    	priority_queue<pii,vector<pii>,greater<pii>> q;
    	int now = 0;
    	for(int i = 0;i < a.size();i++){
    		while(now < i && dp[now] <= t[i]) now++;
    		dp[i] = t[i] + (ll)2 * a[now];
    		while(!q.empty()) {
    			pii p = q.top();
    			if(p.fi <= t[i] + 2 * a[p.se + 1])
    			   	q.pop();
    			else {
    				dp[i] = min(dp[i],p.fi);
    				break;
    			}	
    		}
    		if(i + 1 != a.size())q.push(make_pair(dp[i] + 2 * a[i + 1],i));
    	}
    	printf("%lld
    ",dp[a.size() - 1]);
    	}
    }
    

    F.GCD

    题意

    给定数组(a_1...a_n) ,可以至多删除(k(k leq n/2))个数 求最大的GCD({a})

    分析

    怎么利用(k leq n / 2)这个条件,其实这是一个很常见的套路。

    考虑最后留下的(a)数组中至少会有原数组的一半。那么任意指定一个元素,其不存在原数组的概率(p leq 1 / 2)

    如果随机指定10个数均不在原数组中的概率是(p leq (1/2)^{10})

    所以可以随机下标,假定该数必然在原数组中,然后进行计算。

    若该数在原数组中,答案就是它的某个因子。问题变为了求它的每个因子在其他元素中出现次数,答案就是最大的出现次数大于等于(n - k)的因子

    因子个数大概是(n^{1/3})级别,如果直接暴力算的话复杂度(O(a_i^{1/3}n))显然会爆

    所以用类似高维前缀和方法,把质因子看成维数,就可以做到(O(KT)) 处理出质因子个数 (K)为质因子种类个数,(T)为因子个数

    代码

    #include<bits/stdc++.h>
    #define fi first
    #define se second
    using namespace std;
    
    typedef long long ll;
    
    ll rd(){
    	ll x;
    	scanf("%lld",&x);
    	return x;
    }
    
    ll ans;
    int n,k;
    
    void calc(set<ll,greater<ll>> &s,unordered_map<ll,int> &mp,ll x){
    	for(auto it:s){
    		if(!(it % x)) {
    			mp[it / x] += mp[it];
    			s.insert(it / x);
    		}
    	}
    }
    
    inline ll solve(vector<ll> &a,ll x){
    	set<ll,greater<ll>> s;
    	unordered_map<ll,int> mp;
    	for(int i = 0;i < n;i++)
    		mp[__gcd(a[i],x)]++;
    	for(auto it:mp){
    		s.insert(it.fi);
    	}
    	for(ll i = 2;(ll) i * i * i <= x;i++){
    		if(!(x % i)) {
    			while(!(x % i)) x /= i;
    			calc(s,mp,i);
    			//s.insert(i);	
    		}
    	}
    	if(x != 1) {
    		ll tmp = sqrt(x);
    		if(tmp * tmp < x) tmp++;
    		if(tmp * tmp == x) calc(s,mp,tmp);
    		else if((tmp + 1) * (tmp + 1) == x) calc(s,mp,tmp + 1);
    		else {
    			int flag = 0;
    			for(auto it:s){
    				ll g = __gcd(it,x);
    				if(g != 1 && g != x){
    					calc(s,mp,g);
    					calc(s,mp,x / g);
    					flag = 1;
    					break;
    				}
    			}
    			if(!flag) calc(s,mp,x);
    		}
    	}
    	//for(auto it:s){
    	//	cout << it << '
    ';
    	//}
    	for(auto it:s){
    		if(mp[it] >= n - k) return it;
    	}
    	return 1;
    }
    
    int main(){
    	n = rd();
    	k = rd();
    	srand(0);
    	vector<ll> a(n);
    	for(int i = 0;i < n;i++)
    		a[i] = rd();
    	for(int i = 0;i < 20;i++){
    		int pos = ((unsigned long long)rand() * rand()) % n;
    		ans = max(ans,solve(a,a[pos]));
    	}
    	printf("%lld",ans);
    }
    

    J. Subsequence Sum Queries

    题意

    (q)个询问,每次询问([l,r])的满足(sum equiv 0 (mod m))子序列的个数

    分析

    如果没有区间需求,显然是很简单的线性DP 加上询问可以考虑分治,应该算比较典型的分治+DP 见之前写过的博客

    P7482 不条理狂诗曲 分治 DP - MQFLLY - 博客园 (cnblogs.com)

    注意一下边界即可

    代码

    #include<bits/stdc++.h>
    #define fi first
    #define se second
    #define pii pair<pair<int,int>,int>
    #define re register
    
    using namespace std;
    
    typedef long long ll;
    
    ll rd(){
    	ll x;
    	scanf("%lld",&x);
    	return x;
    }
    
    const int maxn = 2e5 + 5;
    const int MOD = 1e9 + 7;
    
    int a[maxn];
    int dp1[maxn][25];
    int dp2[maxn][25];
    int ans[maxn];
    int n,m;
    
    void add(int &a,int b){
    	a += b;
    	if(a >= MOD) a-= MOD;
    }
    
    void solve(int l,int r,vector<pii>& Q){
    	if(l >= r || Q.empty()) {
    		return;
    	}
    	int mid = l + r >> 1;
    	for(int i = l;i <= r;i++)
    		for(int j = 0;j <= m;j++)
    			dp1[i][j] = dp2[i][j] = 0;
    	dp1[mid + 1][0] = 1;
    	for(int i = mid;i >= l;i--)
    		for(int j = 0;j < m;j++)
    			add(dp1[i][j],dp1[i + 1][j]),add(dp1[i][(a[i] + j) % m],dp1[i + 1][j]);
    	dp2[mid][0] = 1;
    	for(int i = mid + 1;i <= r;i++)
    		for(int j = 0;j < m;j++)
    			add(dp2[i][j],dp2[i - 1][j]),add(dp2[i][(a[i] + j) % m],dp2[i - 1][j]);
    	/*
    	for(int i = l;i <= r;i++){
    		for(int j = 0;j < m;j++){
    			cout << "i = " << i  << ' ' << "j = " << j << ' ' << dp1[i][j] << ' ' << dp2[i][j] << '
    '; 
    		}
    	}*/
    	vector<pii> L,R;
    	for(auto it:Q){
    		if(it.fi.fi > mid && it.fi.fi != it.fi.se) R.push_back(it);
    		else if(it.fi.se < mid && it.fi.fi != it.fi.se) L.push_back(it);
    		else if(it.fi.se != it.fi.fi){
    			for(int j = 0;j < m;j++)
    				add(ans[it.se],(ll)dp1[it.fi.fi][j] * dp2[it.fi.se][!j ?  0 : m - j] % MOD);
    		}
    	}
    	solve(l,mid - 1,L);
    	solve(mid + 1,r,R);
    }
    
    int main(){
    	n = rd();
    	m = rd();
    	for(int i = 1;i <= n;i++)
    		a[i] = rd(),a[i] %= m;
    	int q = rd();
    	vector<pii> Q(q + 1);
    	for(int i = 1;i <= q;i++){
    		Q[i].fi.fi = rd();
    		Q[i].fi.se = rd();
    		Q[i].se = i;
    		if(Q[i].fi.fi == Q[i].fi.se) {
    			a[Q[i].fi.fi] % m == 0 ? ans[Q[i].se] +=2 : ans[Q[i].se]++;
    		}
    	}
    	solve(1,n,Q);
    	for(int i = 1;i <= q;i++)
    		printf("%d
    ",ans[i]);	
    }
    

    K. Consistent Occurrences

    题意

    给定一个主串(s),(n)个模式串

    模式串的总长度不超过(1e5)

    求每个模式串能够在主串匹配多少次(不能重合)

    分析

    直接做显然不太好做(AC自动机和SAM可做?)

    注意到最多(1e5)个串,总长不超过(1e5),可以得到结论串长种类是(O(sqrt))级别的

    于是只需要对询问离线后Hash即可,再用(map)记录上次匹配位置和哈希值

    代码

    #include<bits/stdc++.h>
    #define fi first
    #define se second
    #define re register
    
    using namespace std;
    
    typedef long long ull;
    //typedef long long ll;
    
    ull rd(){
    	ull x;
    	scanf("%lld",&x);
    	return x;
    }
    
    const int maxn = 1e5 + 5;
    const ull P1 = 131;
    const ull P2 = 13331;
    const ull MOD1 = 1e9 + 7;
    const ull MOD2 = 1e9 + 9;
    const ull MOD3 = 998244353;
    
    ull p1[maxn],p2[maxn];
    ull f1[maxn],f2[maxn];
    ull H[maxn];
    char s[maxn];
    char ss[maxn];
    
    inline void add1(ull &a,ull b){
    	a += b;
    	if(a >= MOD1) a -= MOD1;
    }
    
    inline void add2(ull &a,ull b){
    	a += b;
    	if(a >= MOD2) a -= MOD2;
    }
    
    inline ull getHash1(int l,int len){
    	return f1[l + len - 1] - f1[l - 1] * p1[len] % MOD1 + MOD1;
    }
    
    inline ull getHash2(int l,int len){
    	return f2[l + len - 1] - f2[l - 1] * p2[len] % MOD2 + MOD2;
    }
    
    int main(){
    	int n = rd();
    	int m = rd();
    	scanf("%s",s + 1);
    	map<pair<ull,ull>,int> mp;
    	map<pair<ull,ull>,int> last;
    	map<int,pair<ull,ull>> H;
    	unordered_map<ull,int> st;
    	p1[0] = p2[0] = 1;
    	for(re int i = 1;i < maxn;i++)	
    		p1[i] = p1[i - 1] * P1 % MOD1,p2[i] = p2[i - 1] * P2 % MOD2;
    	for(re int i = 1;i <= n;i++)
    		f1[i] = f1[i - 1] * P1 % MOD1,add1(f1[i],s[i] - 'a' + 1),f2[i] = f2[i - 1] * P2 % MOD2,add2(f2[i],s[i] - 'a' + 1);
    	for(re int i = 1;i <= m;i++){
    		scanf("%s",ss + 1);
    		int len = strlen(ss + 1);
    		ull tmp1 = 0;
    		ull tmp2 = 0;
    		for(re int j = 1;j <= len;j++){
    			tmp1 = tmp1 * P1 % MOD1,add1(tmp1,ss[j] - 'a' + 1);
    			tmp2 = tmp2 * P2 % MOD2,add2(tmp2,ss[j] - 'a' + 1);
    		}
    		pair<ull,ull> g = make_pair(tmp1,tmp2);
    		mp[g] = 0,last[g] = 0;
    		H[i] = g;
    		st[len] = 1;
    	}
    	for(auto it :st){
    		for(int i = 1;i + it.fi - 1 <= n;i++){
    			ull g1 = getHash1(i,it.fi);
    			ull g2 = getHash2(i,it.fi);
    			if(g1 >= MOD1) g1 -= MOD1;
    			if(g2 >= MOD2) g2 -= MOD2;
    			pair<ull,ull> g = make_pair(g1,g2);
    			if(mp.count(g) && last[g] < i) {
    				last[g] = i + it.fi - 1;
    				mp[g]++;
    			}	
    		}
    	}
    	for(int i = 1;i <= m;i++){
    		printf("%d
    ",mp[H[i]]);
    	}	
    }
    
  • 相关阅读:
    Windows环境下安装PHPUnit
    用nodejs,express,ejs,mongo,extjs实现了简单了网站后台管理系统
    ftp定时下载指定目录或文件脚本
    centos6、7系统初始化脚本
    Centos6系统启动流程
    使用expect登录批量拷贝本地文件到多个目标主机
    AWK
    基础字符的操作示例
    Linux的正则练习
    Linux权限操作(用户和组)
  • 原文地址:https://www.cnblogs.com/hznumqf/p/14985892.html
Copyright © 2011-2022 走看看