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';
}
posted @ 2021-08-25 11:21  wlhp  阅读(73)  评论(0)    收藏  举报