2020 ICPC 沈阳
H题:
题目中车的数量 \(k < 3e5\), 所以可以把每辆车看作一个独立的整体,考虑 \(dp[i]\) 表示前 \(i\) 辆车的花费,那么问题就变成了对每辆车,可以枚举我们的操作就在用这个车的时候买了哪张卡 \(j\),对车的日期排序,通过双指针维护当前车买这张卡能用到的日期 \(r\),那么我们当前车买这张卡能更新到的就是最小的后车的位置也就是\(min(i + k[j], r[j])\),就是 \(dp[i + min(i + k[j], r[j])] = min(dp[i] + c[j])\) 。复杂度就是 \(O(nk)\)。
void run1() {
input:
int n, m, R, p, q;
cin >> n >> m >> R;
for(int i = 1; i <= n; ++ i)
cin >> d[i] >> k[i] >> c[i];
for(int i = 1; i <= m; ++ i) {
cin >> p >> q;
for(int i = 1; i <= q; ++ i) a[++num] = p;
}
run:
sort(a + 1, a + 1 + num);
memset(dp, 0x3f, sizeof dp); dp[0] = 0;
for(int i = 0; i < num; ++ i) {
dp[i + 1] = min(dp[i + 1], dp[i] + R);
for(int j = 1; j <= n; ++ j) {
l[j] ++;
while(r[j] < num && a[r[j] + 1] - a[l[j]] + 1 <= d[j]) r[j] ++;
int nxt = min(r[j], min(i + k[j], num));
dp[nxt] = min(dp[nxt], dp[i] + c[j]);
}
//cout << dp[i] << '\n';
}
output:
cout << dp[num] << '\n';
return ;
}
I:
化简后得到 \(abs(k ~mod~ M * H - k) <= A\)
令\(k = i * M + j -> j * H - (i * M + j) <= A\)
\(abs(j * (H - 1) - i * M) <= A\)
裴蜀可知必须是 \(A_i\) 整除 \(gcd(H - 1, M)\) 为一种可行解
加上绝对值和0总共有 \(A / gcd(H - 1, M) * 2 + 1\) 个可行解
\(i \in [0, H), j \in [0, M)\) 对于每种解个数都是 \(gcd(H - 1, M)\) 个
\(g = gcd(H - 1, M)\)
求解 $j * (H - 1) - i * M = kg -> j * (H - 1) / g - i * M / g = k $
由裴蜀定理可知每个 \(k\) 在 \(i \in [0, (H - 1) / g), j \in [0, M / g)\) 这个区间中一定会只出现一次
这种区间有 \(min(M / (M / g), H / ((H - 1) / g))\) 个
特判A >= H * M / 2时都是 H * M
void run() {
input:
ll H, M, A, ans = 0; cin >> H >> M >> A;
for(int k = 0; k < H * M; ++ k) {
if(abs(k % M * H - k) <= A) {
//cout << k << '\n';
ans ++;
}
}
for(int i = 0; i < H; ++ i) {
for(int j = 0; j < M; ++ j) {
if(abs(j * H - i * M - j) <= A) {
ans ++;
}
}
}
output:
if(A >= H * M / 2) cout << H * M << '\n';
else cout << gcd(H - 1, M) * (A / gcd(H - 1, M) * 2 + 1) << '\n';
}
J题、Descent of Dragons
有n条龙,两种操作,操作一将l、r区间内等级为x的龙等级加一,操作二输出区间l、r内最高等级的龙,初始时所有龙等级均为1
我们对每个等级的龙建立一棵树,树上维护每个龙是否在这个等级出现过,初始level为0的树上所有点值均为1,最后二分查找查询最后一个在l-r区间有值的level树。
那么朴素的想法就是我们对x等级的树进行操作时,先将l到r的数据分裂下来作为一棵树,再把这棵树合并到等级x+1的树,但是这样即使回收结点,结点数依然会爆
所以我们在更新操作时,新建结点存储等级x+1树,将x+1变为这个新建结点,再利用等级x树更新x+1树,且原来x+1树的位置依旧储存原树,这样就保证了各点之间不会其冲突
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
int sum[maxn*40],lson[maxn*40],rson[maxn*40];
int tot;
int T[maxn];
inline void pushup(int x) {
sum[x]=sum[lson[x]]+sum[rson[x]];
}
inline void build(int &rt,int l,int r) {
if(!rt)rt=++tot;
if(l==r) {
sum[rt]=1;
return;
}
int mid=l+r>>1;
build(lson[rt],l,mid);
build(rson[rt],mid+1,r);
pushup(rt);
}
inline void update(int pre,int &rt,int l,int r,int ql,int qr) {
if (ql<=l&&r<=qr) {
rt=pre;
return;
}
int newrt=++tot;
lson[newrt]=lson[rt],rson[newrt]=rson[rt],rt=newrt;
int mid=(l+r)>>1;
if (ql<=mid) update(lson[pre],lson[rt],l,mid,ql,qr);
if (qr>mid) update(rson[pre],rson[rt],mid+1,r,ql,qr);
pushup(rt);
}
inline int query(int rt,int l,int r,int ql,int qr) {
if(!rt)return 0;
if(ql<=l&&r<=qr) {
return sum[rt];
}
int mid=l+r>>1;
int sum=0;
if(ql<=mid)sum+=query(lson[rt],l,mid,ql,qr);
if(qr>mid)sum+=query(rson[rt],mid+1,r,ql,qr);
return sum;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n,m;
cin>>n>>m;
build(T[0],1,n);
for(int i=1; i<=m; i++) {
int op;
cin>>op;
if(op==1) {
int l,r,x;
cin>>l>>r>>x;
int tmp=0;
update(T[x],T[x+1],1,n,l,r);
} else {
int l,r;
cin>>l>>r;
int L=0,R=i;
int res=0;
while(L<=R) {
int mid=L+R>>1;
if(query(T[mid],1,n,l,r))L=mid+1,res=mid;
else R=mid-1;
}
cout<<res<<"\n";
}
}
}
M题:
题意就是说对于每个问题的子集,其中的 \(n\) 个串两两不相同的个数大于等于 \(k\) 算一个贡献
那么两个串相同就等价异或为0,把串变成数值,求 \(f[s] = \frac 1 2\sum_{i^j=s}num[i] * num[j]\)用fwt
我们实际要求的集合T,只要和S有交,就可以被F(s)贡献
\(g[T] = \sum_{s & t}F(s) = n * n - \sum_{!(s & t)}F(s)\)
这样就变成了子集和的问题,用sos做
#define int long long
#define Xor
//#define Nxor
//#define Or
//#define And
const int mod = 1e9 + 7;
int ksm(int a, int b) {
int res = 1;
for(; b; b >>= 1, a = a * a)
if(b & 1) res = res * a;
return res;
}
struct FWT{
const int inv2 = ksm(2, mod - 2);
int l;
void init(int n) {
l = 2;
while(l < n) l <<= 1;//l <= n?
}
inline int qmod(int x) {
while(x >= mod) x -= mod;
return x;
}
void fwt(vector<int> &a, int opt) {
a.resize(l);//l + 1?
int x, y;
for(int k = 2; k <= l; k <<= 1) {
int mid = k >> 1;
for(int i = 0; i < l; i += k) {
for(int j = i, up = i + mid; j < up; ++ j) {
#ifdef Or
a[j + mid] = qmod(a[j + mid] + opt * a[j] + mod);
#endif
#ifdef And
a[j] = qmod(a[j] + opt * a[j + mid] + mod);
#endif
#ifdef Xor
x = a[j], y = a[j + mid];
//a[j] = qmod(x + y);
//a[j + mid] = qmod(x - y + mod);
//if(opt == -1) a[j] = 1ll * a[j] * inv2 % mod, a[j + mid] = 1ll * a[j + mid] * inv2 % mod;
a[j] = x + y;
a[j + mid] = x - y;
if(opt == -1) a[j] >>= 1, a[j + mid] >>= 1;
#endif
#ifdef Nxor
x = a[j], y = a[j + mid];
a[j] = qmod(y - x + mod);
a[j + mid] = qmod(x + y);
if(opt == -1) a[j] = 1ll * a[j] * inv2 % mod, a[j + mid] = 1ll * a[j + mid] * inv2 % mod;
#endif
}
}
}
}
}f;
/*
4 3 3
BBB
BAA
BBB
ABB
BBA
2 3 2
ABA
ABB
3 3 3
ABA
BAB
AAA
3 3 3
BBA
BAB
BBB
*/
string s;
bool vis[1 << 20];
int a[22], b[22], ff[1 << 20];
void run() {
input:
int n, m, k;
cin >> n >> m >> k;
vector<ll> a1(1 << m);
for(int i = 0; i < n; ++ i) {
cin >> s;
int x = 0;
for(int j = 0; j < m; ++ j)
x <<= 1, x |= s[j] - 'A';
a1[x] ++;
}
for(int i = 0; i < 1 << m; ++ i) cout << a1[i] << ' '; cout << '\n';
f.init(1 << m);
f.fwt(a1, 1);
for(int i = 0; i < 1 << m; ++ i) a1[i] *= a1[i];
f.fwt(a1, -1);
for(int i = 0; i < 1 << m; ++ i) cout << a1[i] << ' '; cout << '\n';
for(int i = 0; i < 1 << m; ++ i) cout << a1[i] << ' '; cout << '\n';
//sos_dp 用于解决子集和问题,基于一种容斥模型,nlog复杂度
//比如说3这个集合的值应该是3 + 2 + 1 + 0,他和他的所有子集的值,那么如果我们前面已经算出了2和1和0,那么显然
//3就是2+1-0,因为2=2+0,1=1+0。就是容斥模型求子集和
a1[0] -= n;
for(int i = 0; i < m; ++ i) for(int j = 0; j < 1 << m; ++ j) {
if(j & 1 << i) a1[j] += a1[j ^ 1 << i];
}
//两两异或总共用n^2个,去掉自己异或自己的还有n(n-1),去掉重复的还有n(n-1)/2,要大于等于k
for(int i = 0; i < 1 << m; ++ i) a1[i] >>= 1;
for(int i = 0; i < 1 << m; ++ i) cout << a1[i] << ' '; cout << '\n';
int ans = 0, sz = n * (n - 1) / 2 - k;
for(int i = 0; i < 1 << m; ++ i) if(sz >= a1[i ^ (1 << m)]) ans ++;
output:
cout << ans << '\n';
}

浙公网安备 33010602011771号