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
题解
伪阅读理解题
问置信区间重叠前有几个人成功抢到菜。 按关键词为 升序排序后,比较相邻两个,如果则有重叠
证明:假设前面都没有重叠,那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);
}