2022暑期hdu2

1 Static Query on Tree

题解

解法一: 树链剖分,对于集合 A, B,将根到该点打上 a 标记 / b 标记;对于集合 C,将该点的子树打上 c 标记;最后统 计同时有 3 种标记的节点个数。用线段树可以维护。

解法二(正解): 考虑一个简化的问题 1,如果只有 A 的限制,也就是求一个集合里所有节点可以走到的节点数。首先对 A 根据 节点在 dfs 序中的位置排序,然后将深度加起来,减去相邻节点 lca 的深度之和。 在考虑一个稍复杂的问题 2,如果只有 A, B 的限制。很显然这个问题即两个问题 1 的交集,但是直接算交集比 较困难。我们有公式,两个集合大小之和等于交集大小加并集大小,因此只要算出 A, B, A B 在问题 1 的解, 就能推断出并集的大小了。 最后就是原问题,其实就是分成了多个子树,在这些子树上进行问题 2 的操作。

标程

#include <bits/stdc++.h>
using namespace std;

const int N = 200010;
int n, q;
vector<int> e[N];
vector<int> a, b, c;
int l[N], r[N];
int cnt;

namespace lca {
    int dep[N], son[N], sz[N], top[N], fa[N];
    void dfs1(int x) {
        sz[x] = 1;
        son[x] = -1;
        for (auto p : e[x]) {
            if (p == fa[x]) continue;
            fa[p] = x; dep[p] = dep[x] + 1;
            dfs1(p);
            sz[x] += sz[p];
            if (son[x] == -1 || sz[son[x]] < sz[p])
                son[x] = p;
        }
    }
    void dfs2(int x, int tv) {
        top[x] = tv;
        if (son[x] == -1) return;
        dfs2(son[x], tv);
        for (auto p : e[x]) {
            if (p == fa[x] || p == son[x]) continue;
            dfs2(p, p);
        }
    }
    void init(int s) {
        fa[s] = -1; dep[s] = 0;
        dfs1(s);
        dfs2(s, s);
    }
    int lca(int x, int y) {
        while (top[x] != top[y])
            if (dep[top[x]] >= dep[top[y]]) x = fa[top[x]];
            else y = fa[top[y]];
        return dep[x] < dep[y] ? x : y;
    }
}

void dfs(int x) {
    l[x] = ++cnt;
    for (int p : e[x]) {
        dfs(p);
    }
    r[x] = cnt;
}

int calc(vector<int> &a, const vector<int> &c) {
    sort(a.begin(), a.end(), [](int x, int y) {
        return l[x] < l[y];
    });
    int left = 0, res = 0;
    for (int cc : c) {
        while (left < (int)a.size() && l[a[left]] < l[cc]) {
            left++;
        }
        int right = left;
        while (right < (int)a.size() && l[a[right]] <= r[cc]) {
            right++;
        }
        for (int i = left; i < right; i++) {
            res += lca::dep[a[i]] - lca::dep[cc] + 1;
        }
        for (int i = left; i < right - 1; i++) {
            res -= lca::dep[lca::lca(a[i], a[i + 1])] - lca::dep[cc] + 1;
        }

        left = right;
    }

    return res;
}

void solve() {
    scanf("%d%d", &n, &q);

    for (int i = 1; i <= n; i++) {
        e[i].clear();
    }
    cnt = 0;

    for (int i = 2; i <= n; i++) {
        int fa;
        scanf("%d", &fa);
        e[fa].push_back(i);
    }
    dfs(1);
    lca::init(1);

    while (q--) {
        {
            int A, B, C;
            scanf("%d%d%d", &A, &B, &C);
            a.assign(A, 0);
            b.assign(B, 0);
            c.assign(C, 0);
        }
        for (int i = 0; i < (int)a.size(); i++) {
            scanf("%d", &a[i]);
        }
        for (int i = 0; i < (int)b.size(); i++) {
            scanf("%d", &b[i]);
        }
        for (int i = 0; i < (int)c.size(); i++) {
            scanf("%d", &c[i]);
        }

        sort(c.begin(), c.end(), [](int x, int y) {
            return l[x] < l[y];
        });
        int pre = 0;
        for (int i = 1; i < (int)c.size(); i++) {
            if (l[c[pre]] <= l[c[i]] && l[c[i]] <= r[c[pre]]) {}
            else {
                pre++;
                c[pre] = c[i];
            }
        }
        c.erase(c.begin() + pre + 1, c.end());

        int ans = calc(a, c) + calc(b, c);
        for (int i : b) {
            a.push_back(i);
        }
        ans -= calc(a, c);
        printf("%d\n", ans);
    }
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        solve();
    }
    return 0;
}

 2 C++ to Python

题解

签到,只要无视字母、下划线、冒号后输出即可。

标程

#include <bits/stdc++.h>
using namespace std;

const int N = 200010;
char s[N];

void solve() {
    scanf("%s", s);
    for (int i = 0; s[i]; i++) {
        if (s[i] != ':' && s[i] != '_' && !isalpha(s[i])) {
            putchar(s[i]);
        }
    }
    puts("");
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        solve();
    }
    return 0;
}

1003 Copy

题解

一个修改操作对后续的查询操作的影响:如果x<=r就没影响,如果x>r,相当于查询x-(r-l+1),然后去掉修改操作。 因此可以离线,倒着处理所有修改操作,每个修改操作都让它之后的所有x>r的询问 x -= r - l + 1 。 但是这么处理还是O(n^2)的。考虑到我们只要答案的异或和,就有,两个相同的查询可以抵消,因此同一位置 至多只会查询一次。这样每个位置用 1 bit 的信息表示即可,也就是 bitset。 我们令 dp 第 i 位为 1 表示答案需要对 a[i] 异或。倒着遍历所有操作,如果是查询操作, dp[x] ^= 1 ,如果是 修改操作,那么就让 r+1..n 这些比特右移 r-l+1

标程

#include <bits/stdc++.h>
using namespace std;

const int N = 100010;

int a[N];
bitset<N> dp, low, high;
array<int, 3> v[N];

void solve() {
    int n, q;
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    for (int i = 1; i <= q; i++) {
        scanf("%d", &v[i][0]);
        if (v[i][0] == 1) {
            scanf("%d%d", &v[i][1], &v[i][2]);
        } else {
            scanf("%d", &v[i][1]);
        }
    }
    dp = 0;
    for (int i = q; i >= 1; i--) {
        if (v[i][0] == 1) {
            int l = v[i][1], r = v[i][2];
            low = dp & (~bitset<N>(0) >> (N - r - 1));
            high = dp & (~bitset<N>(0) << (r + 1));
            dp = low ^ (high >> (r + 1 - l));
        } else {
            int x = v[i][1];
            dp[x] = !dp[x];
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        if (dp[i]) {
            ans ^= a[i];
        }
    }
    printf("%d\n", ans);
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        solve();
    }
    return 0;
}

5 Slayers Come

题解

显然,每个技能都可以击败一个区间的野怪,我们先处理出每个技能可以击败的区间。

先计算区间的右端点:将所有的a[i]-b[i+1]从大到小排序,再将所有的技能按R[j]从大到小排序,依次 扫描每个技能(R[j]相同的一起处理)。对于a[i]-b[i+1]>=R[j]的所有i,则用并查集将i和i+1合 并。第j个区间的右端点就是X[j]所在连通块的最右值。 左端点同理。

接下来就是区间重复覆盖计数问题:n个位置,m个区间,求选出的区间能够重复覆盖[1,n]的方案数。 设dp[i]表示恰好覆盖了区间[1,i]的方案数。 我们将所有区间按照右端点从小到大进行排序,依次扫描每个区间,考虑一个区间[l,r]对dp数组的贡献。

标程

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class T>inline void MAX(T &x,T y){if(y>x)x=y;}
template<class T>inline void MIN(T &x,T y){if(y<x)x=y;}
template<class T>inline void rd(T &x){
	x=0;char o,f=1;
	while(o=getchar(),o<48)if(o==45)f=-f;
	do x=(x<<3)+(x<<1)+(o^48);
	while(o=getchar(),o>47);
	x*=f;
}
template<class T>inline void print(T x,bool op=1){
	static int top,stk[105];
	if(x<0)x=-x,putchar('-');
	if(x==0)putchar('0');
	while(x)stk[++top]=x%10,x/=10;
	while(top)putchar(stk[top--]+'0');
	putchar(op?'\n':' ');
}
const int M=1e5+5;
const int mod=998244353;
int cas,n,m,A[M],B[M];
struct node{
	int x,vl,vr;
	int l,r;
}Q[M];
pair<int,int>diff[M];
int fa[M];
int getfa(int x){
	return x==fa[x]?x:fa[x]=getfa(fa[x]);
}

int tree[M<<2],lazy_mul[M<<2],lazy_add[M<<2];
void build(int l=0,int r=n,int p=1){
	lazy_mul[p]=1;
	lazy_add[p]=0;
	tree[p]=0;
	if(l==r)return;
	int mid=l+r>>1;
	build(l,mid,p<<1);
	build(mid+1,r,p<<1|1);
}
void down(int p,int l,int r){
	if(lazy_mul[p]!=1){
		tree[p<<1]=1ll*tree[p<<1]*lazy_mul[p]%mod;
		lazy_mul[p<<1]=1ll*lazy_mul[p<<1]*lazy_mul[p]%mod;
		lazy_add[p<<1]=1ll*lazy_add[p<<1]*lazy_mul[p]%mod;
		tree[p<<1|1]=1ll*tree[p<<1|1]*lazy_mul[p]%mod;
		lazy_mul[p<<1|1]=1ll*lazy_mul[p<<1|1]*lazy_mul[p]%mod;
		lazy_add[p<<1|1]=1ll*lazy_add[p<<1|1]*lazy_mul[p]%mod;
		lazy_mul[p]=1;
	}
	if(lazy_add[p]){
		int mid=l+r>>1;
		tree[p<<1]=(tree[p<<1]+1ll*(mid-l+1)*lazy_add[p])%mod;
		lazy_add[p<<1]=(lazy_add[p<<1]+lazy_add[p])%mod;
		tree[p<<1|1]=(tree[p<<1|1]+1ll*(r-mid)*lazy_add[p])%mod;
		lazy_add[p<<1|1]=(lazy_add[p<<1|1]+lazy_add[p])%mod;
		lazy_add[p]=0;
	}
}
void update(int a,int b,int mul,int add,int l=0,int r=n,int p=1){
	if(l>b||r<a)return;
	if(l>=a&&r<=b){
		if(mul!=1){
			tree[p]=1ll*tree[p]*mul%mod;
			lazy_mul[p]=1ll*lazy_mul[p]*mul%mod;
			lazy_add[p]=1ll*lazy_add[p]*mul%mod;
		}
		if(add){
			tree[p]=(tree[p]+1ll*(r-l+1)*add)%mod;
			lazy_add[p]=(lazy_add[p]+add)%mod;
		}
		return;
	}
	down(p,l,r);
	int mid=l+r>>1;
	update(a,b,mul,add,l,mid,p<<1);
	update(a,b,mul,add,mid+1,r,p<<1|1);
	tree[p]=(tree[p<<1]+tree[p<<1|1])%mod;
}
int query(int a,int b,int l=0,int r=n,int p=1){
	if(l>b||r<a)return 0;
	if(l>=a&&r<=b)return tree[p];
	down(p,l,r);
	int mid=l+r>>1;
	return (query(a,b,l,mid,p<<1)+query(a,b,mid+1,r,p<<1|1))%mod;
}

signed main(){
#ifndef ONLINE_JUDGE
//	freopen("jiedai.in","r",stdin);
//	freopen("jiedai.out","w",stdout);
#endif
	rd(cas);
	while(cas--){
		rd(n),rd(m);
		for(int i=1;i<=n;i++)rd(A[i]),rd(B[i]);
		for(int i=1;i<=m;i++)rd(Q[i].x),rd(Q[i].vl),rd(Q[i].vr);
		
		for(int i=1;i<=n;i++)fa[i]=i;
		for(int i=1;i<n;i++)diff[i]=make_pair(A[i]-B[i+1],i);
		sort(diff+1,diff+n);
		sort(Q+1,Q+1+m,[](node &a,node &b){
			return a.vr>b.vr;
		});
		int now=n-1;
		for(int i=1;i<=m;i++){
			while(now>=1&&diff[now].first>=Q[i].vr){
				int pos=diff[now].second;
				fa[pos]=pos+1;
				now--;
			}
			Q[i].r=getfa(Q[i].x);
		}
		
		for(int i=1;i<=n;i++)fa[i]=i;
		for(int i=1;i<n;i++)diff[i]=make_pair(A[i+1]-B[i],i);
		sort(diff+1,diff+n);
		sort(Q+1,Q+1+m,[](node &a,node &b){
			return a.vl>b.vl;
		});
		now=n-1;
		for(int i=1;i<=m;i++){
			while(now>=1&&diff[now].first>=Q[i].vl){
				int pos=diff[now].second;
				fa[pos+1]=pos;
				now--;
			}
			Q[i].l=getfa(Q[i].x);
		}
		
		sort(Q+1,Q+1+m,[](node &a,node &b){
			return a.r<b.r;
		});
		build();
		update(0,0,1,1);
		for(int i=1;i<=m;i++){
			int l=Q[i].l,r=Q[i].r;
			update(r,r,1,query(l-1,r));
			update(0,l-2,2,0);
		}
		print(query(n,n));
	}
	return (0-0);
}

7 Snatch Groceries

题解

伪阅读理解题

问置信区间重叠前有几个人成功抢到菜。 按关键词为 升序排序后,比较相邻两个,如果latest_i>=earlist_i则有重叠

证明:假设前面都没有重叠,那i之后最有可能和i重叠的一定是earliest最小的,反之如果最小的都没有 重叠,那么之后必然不可能有一个区间与i重叠。所以这样排序处理没有问题。

标程

#include <bits/stdc++.h>

using namespace std;

signed main() {
  int n, T;
  vector<pair<int, int>> t;
  scanf("%d", &T);
  while (T--) {
    scanf("%d", &n);
    t.resize(n + 1);
    for (int i = 0; i < n; ++i) {
      scanf("%d%d", &t[i].first, &t[i].second);
    }
    t[n] = make_pair(1234567890, 0);
    sort(t.begin(), t.end());
    int ans = 0;
    for (int i = 0; i < n; ++i) {
      if (t[i].second >= t[i + 1].first) break;
      ++ans;
    }
    printf("%d\n", ans);
  }
  return 0;
}

9 ShuanQ

题解

P * Q = 1 mod M

P * Q - 1 = k * M, k >= 1

M是kM的一个比P,Q大的质因子

最多只有一个质因子满足要求,如果有多个满足要求的质因子M1,M2那么kM=M1*M2>P*Q,矛盾

标程

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

int main() {
  int T, encrypted_data, P, Q;
  scanf("%d", &T);
  while (T--) {
    scanf("%d%d%d", &P, &Q, &encrypted_data);
    ll kM = 1ll * P * Q - 1, raw_data = -1, M;
    for (ll i = 2; i * i <= kM; ++i) {
      if (kM % i) continue;
      while (kM % i == 0) kM /= i;
      M = i;
    }
    if (kM > 1) M = kM;
    if (P < M && Q < M) raw_data = 1ll * encrypted_data * Q % M;
    if (~raw_data) printf("%lld\n", raw_data);
    else puts("shuanQ");
  }
}

11 DOS Card

题解

一对匹配的值 左 左 右 右 线段树上维护以下8个变量: 区间最大值 区间次大值 区间最小值 区间次小值 选了一对的最大值 选了两对的最大值 (一对的值 剩下的最大值)的最大值 (一对的值 剩下的最小值)的最大值

标程

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class T>inline void MAX(T &x,T y){if(y>x)x=y;}
template<class T>inline void MIN(T &x,T y){if(y<x)x=y;}
template<class T>inline void rd(T &x){
	x=0;char o,f=1;
	while(o=getchar(),o<48)if(o==45)f=-f;
	do x=(x<<3)+(x<<1)+(o^48);
	while(o=getchar(),o>47);
	x*=f;
}
template<class T>inline void print(T x,bool op=1){
	static int top,stk[105];
	if(x<0)x=-x,putchar('-');
	if(x==0)putchar('0');
	while(x)stk[++top]=x%10,x/=10;
	while(top)putchar(stk[top--]+'0');
	putchar(op?'\n':' ');
}
const int M=1e5+5;
int cas,n,m,A[M];
struct node{
	int len;
	ll max1,max2,min1,min2,res1,res2,res1_max,res1_min;
	node(int len=0){
		this->len=len;
		max1=max2=-1e18;
		min1=min2=1e18;
		res1=res2=res1_max=res1_min=-1e18;
	}
	void update_max(ll v){
		if(v>max1)max2=max1,max1=v;
		else if(v>max2)max2=v;
	}
	void update_min(ll v){
		if(v<min1)min2=min1,min1=v;
		else if(v<min2)min2=v;
	}
	node operator +(const node &A)const{
		if(len==0)return A;
		if(A.len==0)return *this;
		
		node T(len+A.len);
		
		T.update_max(max1);
		T.update_max(max2);
		T.update_max(A.max1);
		T.update_max(A.max2);
		
		T.update_min(min1);
		T.update_min(min2);
		T.update_min(A.min1);
		T.update_min(A.min2);
		
		MAX(T.res2,res2);
		MAX(T.res2,A.res2);
		MAX(T.res2,res1+A.res1);
		MAX(T.res2,max1+max2-A.min1-A.min2);
		MAX(T.res2,res1_max-A.min1);
		MAX(T.res2,max1+A.res1_min);
		
		MAX(T.res1,res1);
		MAX(T.res1,A.res1);
		MAX(T.res1,max1-A.min1);
		
		MAX(T.res1_max,res1_max);
		MAX(T.res1_max,A.res1_max);
		MAX(T.res1_max,res1+A.max1);
		MAX(T.res1_max,A.res1+max1);
		if(A.len>=2)MAX(T.res1_max,max1-A.min1+A.max1);
		if(len>=2)MAX(T.res1_max,max1-A.min1+max2);
		
		MAX(T.res1_min,res1_min);
		MAX(T.res1_min,A.res1_min);
		MAX(T.res1_min,res1-A.min1);
		MAX(T.res1_min,A.res1-min1);
		if(A.len>=2)MAX(T.res1_min,max1-A.min1-A.min2);
		if(len>=2)MAX(T.res1_min,max1-A.min1-min1);
		
		return T;
	}
}tree[M<<2];
void build(int l=1,int r=n,int p=1){
	if(l==r){
		tree[p]=node(1);
		tree[p].max1=tree[p].min1=1ll*A[l]*A[l];
		return;
	}
	int mid=l+r>>1;
	build(l,mid,p<<1);
	build(mid+1,r,p<<1|1);
	tree[p]=tree[p<<1]+tree[p<<1|1];
}
node query(int a,int b,int l=1,int r=n,int p=1){
	if(l>b||r<a)return node(0);
	if(l>=a&&r<=b)return tree[p];
	int mid=l+r>>1;
	return query(a,b,l,mid,p<<1)+query(a,b,mid+1,r,p<<1|1);
}

signed main(){
#ifndef ONLINE_JUDGE
//	freopen("jiedai.in","r",stdin);
//	freopen("jiedai.out","w",stdout);
#endif
	rd(cas);
	while(cas--){
		rd(n),rd(m);
		for(int i=1;i<=n;i++)rd(A[i]);
		build();
		while(m--){
			int l,r;
			rd(l),rd(r);
			print(query(l,r).res2);
		}
	}
	return (0-0);
}

1012 Luxury cruise ship

题解

对于N较小的询问,用背包预处理出答案。

如果N较大,先用 365 将 N 减小到背包预处理的范围内,再算出答案。

标程

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class T>inline void MAX(T &x,T y){if(y>x)x=y;}
template<class T>inline void MIN(T &x,T y){if(y<x)x=y;}
template<class T>inline void rd(T &x){
	x=0;char o,f=1;
	while(o=getchar(),o<48)if(o==45)f=-f;
	do x=(x<<3)+(x<<1)+(o^48);
	while(o=getchar(),o>47);
	x*=f;
}
template<class T>inline void print(T x,bool op=1){
	static int top,stk[105];
	if(x<0)x=-x,putchar('-');
	if(x==0)putchar('0');
	while(x)stk[++top]=x%10,x/=10;
	while(top)putchar(stk[top--]+'0');
	putchar(op?'\n':' ');
}
const int M=1e7+5;
ll cas,n;
int dp[M];
void check(int &x,int y){
	if(x==-1||y<x)x=y;
}
signed main(){
#ifndef ONLINE_JUDGE
//	freopen("jiedai.in","r",stdin);
//	freopen("jiedai.out","w",stdout);
#endif
	memset(dp,-1,sizeof(dp));
	dp[0]=0;
	for(int i=0;i<M;i++)if(~dp[i]){
		if(i+7<M)check(dp[i+7],dp[i]+1);
		if(i+31<M)check(dp[i+31],dp[i]+1);
		if(i+365<M)check(dp[i+365],dp[i]+1);
	}
	rd(cas);
	while(cas--){
		rd(n);
		ll tmp=0;
		if(n>M)tmp=(n-M)/365+1,n-=tmp*365;
		print(tmp+dp[n]);
	}
	return (0-0);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值