2022NOIP A层联测26 乘筛积 放进去 最长路径 生成树的传说

T1[转化枚举角度]给出长度是n的a序列,和长度是m的b序列,给出Q组询问\([p,q]\),求\(\sum_{p*i+q*j=C}^{}ai*bj\)。(n,m,C,q<=3e5)

考场

上来想枚举,对于\(max(p,q)>=\sqrt{C}\),直接暴力枚举就好了,但是\(max(p,q)<\sqrt{C}?\),没思路,算了,看数据,发现好多0啊,而且不是0的数大量重复,于是记忆化一下询问就直接A了

正解

其实做法没错,但是要分析一下为什么对于\(max(p,q)<\sqrt{C}\)离线处理暴力算是对的。
\(time=\sum_{p \epsilon [1,\sqrt{C}]} \sum_{q\epsilon[p,\sqrt{C}]}\frac{C}{q}\),发现后面的东西接近C,所以等效成\(\sqrt{C}*\sum q\),复杂度就是对的。

点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define chu printf
#define rint register int
inline ll re() {
    ll x = 0, h = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            h = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * h;
}
const int N = 3e5 + 100;
const int mod = 998244353;
int n, m, T, C;
int a[N], b[N];
map<pair<int, int>, int> mp;  //记录所有询问,统计计算答案
int id, ans[N];
struct node {
    int p, q, bl;
} que[N];
inline int gcd(int x, int y) {
    if (!y)
        return x;
    return gcd(y, x % y);
}
int main() {
    // freopen("sedge3.in","r",stdin);
    // freopen("1.out","w",stdout);
    freopen("sedge.in", "r", stdin);
    freopen("sedge.out", "w", stdout);
    n = re(), m = re(), C = re();
    for (rint i = 1; i <= n; ++i) a[i] = re();
    for (rint i = 1; i <= m; ++i) b[i] = re();
    ans[0] = 0;
    T = re();
    for (rint i = 1; i <= T; ++i) {
        que[i].p = re(), que[i].q = re();
        int gd = gcd(que[i].p, que[i].q);
        if (C % gd)  //无法凑出,没有正整数解
        {
            que[i].bl = 0;
            continue;
        }
        if (1ll * que[i].p * n + 1ll * que[i].q * m < C) {
            que[i].bl = 0;
            continue;
        }
        if (mp.find(make_pair(que[i].p, que[i].q)) == mp.end()) {
            que[i].bl = id + 1;
            ++id;
            mp[make_pair(que[i].p, que[i].q)] = id;
        }  //还有exgcd优化,看情况加不加!!!
        else
            que[i].bl = mp[make_pair(que[i].p, que[i].q)];
    }
    for (pair<pair<int, int>, int> Ele : mp)  //遍历元素,暴力求解
    {
        pair<int, int> ele = Ele.first;
        if (ele.first > ele.second) {
            int sum = 0;
            int bj = C / ele.first;  // bj是fir最多用
            bj = min(bj, n);
            for (rint i = 0; i <= bj; ++i) {
                int res = C - i * ele.first;
                if (res % ele.second || (res / ele.second > m))
                    continue;
                sum = (1ll * sum + 1ll * a[i] * b[res / ele.second] % mod) % mod;
            }
            ans[Ele.second] = sum;
        } else {
            int sum = 0;
            int bj = C / ele.second;  // bj是fir最多用
            bj = min(bj, m);
            for (rint i = 0; i <= bj; ++i) {
                int res = C - i * ele.second;
                if (res % ele.first || (res / ele.first > n))
                    continue;
                sum = (1ll * sum + 1ll * a[res / ele.first] * b[i] % mod) % mod;
            }
            ans[Ele.second] = sum;
        }
    }
    for (rint i = 1; i <= T; ++i) chu("%d\n", ans[que[i].bl]);
    return 0;
}
/*
3 4 5
1 2 3
1 2 3 4
2
1 1
1 2

*/

T2[SOS DP]给出m种商店和n个物品,每种物品在不同商店都有不同价值,如果在某个商店买了东西就要付出额外bi的代价,在第j个商店买i物品代价是a[i][j].求买全所有物品的最小花费。(n<=1e5,m<=25)

考场

这不就无脑DP?一算\(O(n*2^m*m)\),想想本质,其实枚举商店后决策就确定了,但是复杂度不减少...
于是对于<=1e8的复杂度点直接暴力
剩下的贪心:
先忽略b进行a的选择,然后考虑把商店按照b从大到小排序,依次删除商店,购买的物品选择当前最优解去买(这其实是假的),
发现第3个数据过不了,然后把b排序后的商店小的那一半随机打乱,然后它过了。
然后它A了
神奇...

正解

用到子集DP,其实就是快速计算出选择某商店集合,对每个物品的已选商店最小价值加和。
\(f[S]表示对于不能够选择S中的物品的最小花费\)
\(g[S]:先预处理出针对每个物品把商店按照价格sort依次加入到S集合中的花费,对于i物品,新加入j商店的代价就是\)
\(a_{i,j+1}-a_{i,j},表示j不能作为选择,那么下一个最优可选集合就是第一个大于它的,差值就是这个\)
\(但是这样只是计算出对于每个商店价格连续的从小到大集合,比如a1<a2<a3<a4,那么1000,1100,1110,1111被累加\)
\(但是1101,1001没有计算,这时就需要f[i]=\sum g[s],s\epsilon i\),子集和,针对每个物品累加有用的只是其中包含的从min开始连续段(否则一定选择最小断点),差分的值代表含义就是累加后就是最小未选物品的价值。
\(比如1101,会被1100,1000,0000子集累加,刚好是第3小的价值,0001虽然有但是因为不含min所以价值0\)

点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define chu printf
#define rint register int
inline ll re() {
    ll x = 0, h = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            h = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * h;
}
const int N = 1e5 + 100, M = 27;
int a[N][M], b[M], n, m, bit[M], buc[M], bl[N], p[M];
vector<int> g[M];
inline bool cmp(int x, int y) { return b[x] > b[y]; }
inline void solve_slow() {
    int mx = (1 << m) - 1;
    ll ans = 1e18;
    for (rint s = 1; s <= mx; ++s)  // s是选择的AI集合
    {
        ll cost = 0;
        bit[0] = 0;
        for (rint j = 1; j <= m; ++j) {
            if (s & (1 << (j - 1)))
                cost += b[j], bit[++bit[0]] = j;
        }
        for (rint i = 1; i <= n; ++i) {
            int minn = 2e9;
            for (rint J = 1, j = bit[J]; J <= bit[0]; J++, j = bit[J]) {
                minn = min(minn, a[i][j]);
            }
            cost += minn;
        }
        ans = min(ans, cost);
    }
    chu("%lld\n", ans);
}
int main() {
    // freopen("putin1.in","r",stdin);
    // freopen("1.out","w",stdout);
    freopen("putin.in", "r", stdin);
    freopen("putin.out", "w", stdout);
    srand(time(0));
    n = re(), m = re();
    for (rint i = 1; i <= n; ++i)
        for (rint j = 1; j <= m; ++j) a[i][j] = re();
    for (rint i = 1; i <= m; ++i) b[i] = re();
    if (1ll * n * (1ll << m) * m <= 1e8)  //勉强跑过
    {
        solve_slow();
        return 0;
    }
    ll his = 1e18;
    ll ans = 0;
    for (rint i = 1; i <= n; ++i) {
        ll minn = 1e18;
        int from = 0;
        for (rint j = 1; j <= m; ++j) {
            if (minn > a[i][j])
                minn = a[i][j], from = j;
        }
        bl[i] = from;
        g[from].push_back(i);
        buc[from]++;
        ans += minn;
        if (buc[from] == 1)
            ans += b[from];
        // chu("after :%d ans is:%d(from;%d minn:%lld b[]:%lld)\n",i,ans,from,minn,b[from]);
    }
    his = min(his, ans);
    // chu("his;%lld\n",his);
    for (rint i = 1; i <= m; ++i) p[i] = i;
    sort(p + 1, p + 1 + m, cmp);
    for (rint I = 1; I < m; ++I)  //从大到小删除集合中b大的数,表示尝试只选择[i+1,m]的b
    {
        int i = p[I];
        // chu("buc[%d]:%d\n",i,buc[i]);
        if (!buc[i])
            continue;  //没有就不用删除
                       //  chu("del:%d\n",i);
        for (rint ele : g[i]) {
            // ele另谋高就
            ll minn = 1e18;
            int from = 0;
            for (rint J = I + 1; J <= m; ++J) {
                int j = p[J];
                if (!buc[j]) {
                    if (minn > b[j] + a[ele][j])
                        minn = b[j] + a[ele][j], from = j;
                } else {
                    if (minn > a[ele][j])
                        minn = a[ele][j], from = j;
                }
            }
            bl[ele] = from;
            g[from].push_back(ele);
            buc[from]++;
            ans = ans + minn - a[ele][i];
            // chu("add:%lld\n",minn-a[ele][i]);
        }
        ans = ans - b[i];
        his = min(ans, his);
        buc[i] = 0;
        g[i].clear();
        // chu("ans:%lld\n",ans);
    }

    int mid = m / 2;
    random_shuffle(p + mid + 1, p + m + 1);
    //   for(rint i=1;i<=m;++i)chu("f:%d\n",p[i]);
    //   chu("shuffle:%d--%d\n",1+mid,m);
    for (rint i = 1; i <= m; ++i) g[i].clear(), buc[i] = 0;  // chu("p[%d]:%d\n",i,p[i]);
    for (rint i = 1; i <= n; ++i) bl[i] = 0;
    ans = 0;
    for (rint i = 1; i <= n; ++i) {
        ll minn = 1e18;
        int from = 0;
        for (rint j = 1; j <= m; ++j) {
            if (minn > a[i][j])
                minn = a[i][j], from = j;
        }
        bl[i] = from;
        g[from].push_back(i);
        buc[from]++;
        ans += minn;
        if (buc[from] == 1)
            ans += b[from];
        // chu("after :%d ans is:%d(from;%d minn:%lld b[]:%lld)\n",i,ans,from,minn,b[from]);
    }
    his = min(his, ans);
    for (rint I = 1; I < m; ++I)  //从大到小删除集合中b大的数,表示尝试只选择[i+1,m]的b
    {
        int i = p[I];
        // chu("buc[%d]:%d\n",i,buc[i]);
        if (!buc[i])
            continue;  //没有就不用删除
                       //  chu("del:%d\n",i);
        for (rint ele : g[i]) {
            // ele另谋高就
            ll minn = 1e18;
            int from = 0;
            for (rint J = I + 1; J <= m; ++J) {
                int j = p[J];
                if (!buc[j]) {
                    if (minn > b[j] + a[ele][j])
                        minn = b[j] + a[ele][j], from = j;
                } else {
                    if (minn > a[ele][j])
                        minn = a[ele][j], from = j;
                }
            }
            bl[ele] = from;
            g[from].push_back(ele);
            buc[from]++;
            ans = ans + minn - a[ele][i];
            // chu("add:%lld\n",minn-a[ele][i]);
        }
        ans = ans - b[i];
        his = min(ans, his);
        buc[i] = 0;
        g[i].clear();
        // chu("ans:%lld\n",ans);
    }
    chu("%lld", his);
    return 0;
}
/*
 */

\(O(m*2^m)\)

点击查看代码
 #include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 34000000;

int n, m, a[30], lg[maxn], p[30], b[30];
ll sb[maxn], f[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

bool cmp(int x, int y) {return a[x] < a[y];}

int main()
{
    freopen("putin.in", "r", stdin);
    freopen("putin.out", "w", stdout);
    
    n = read(); m = read();
	for(int i=1; i<=m; i++) p[i] = i - 1;
	for(int i=1; i<=n; i++)
	{
		for(int j=0; j<m; j++) a[j] = read();
		sort(p+1, p+1+m, cmp);
		for(int s=0,j=m; j>0; j--)
		{
			f[s] -= a[p[j]]; s |= (1<<p[j]); f[s] += a[p[j]];
		}
	}
	for(int i=0; i<m; i++)
	{
		for(int j=0; j<(1<<m); j+=(1<<i+1))
		{
			for(int k=0; k<(1<<i); k++)
			{
				f[j+k] += f[j+k+(1<<i)];
			}
		}
	}
	for(int i=1; i<=m; i++) b[i] = read();
	for(int i=2; i<(1<<m); i+=i) lg[i] = lg[i>>1] + 1;
	for(int i=1; i<(1<<m); i++) sb[i] = sb[i^(i&-i)] + b[lg[i&-i]+1];
	ll ans = 4e18;
	for(int s=1; s<(1<<m); s++) ans = min(ans, sb[s]+f[s]);
	printf("%lld\n", ans);

    return 0;
}
注意啊,这是Catherine的,待会儿把自己的stick上!

T4[贪心/数据结构维护]给出n个点,m条边的图,每条边边权范围[li,ri],求m条边的边权组成m的一个排列,而且前n-1条边是图的最小生成树的构造方案,要求字典序最小。(n,m<=3500)

考场

没时间了我要打暴力!
<=10的直接阶乘枚举边,l=r的只判断了值不重复,但是有可能不是最小生成树啊!所以5pts跑了

正解

经典贪心:n个数第i个数的值域范围是[li,ri],求给n个数赋值成1-n的排列的构造方案是否存在
按照1--n的值开始分配,每次找到未确定的数中li<=now,ri>=now的ri最小的,赋值成now
构建树后,考虑形成若干对限制条件形如\(wi>wj\),那么对于\(li=max(li,lj+1),rj=min(rj,ri-1)\),构造这样的方案判断合法
可以直接判断是否是最小生成树条件,可以set双指针维护\(O(n*logn)\)扫一遍序列判断无解
接下来,对边按照编号从小到大分配值,对于\(i边可以分配j值等价于\forall L<=j<=R,R>\sum L<=lk<rk<=R\)
于是对每个边分配线段树维护到目前的R,每个位置L的值累计,判断合法。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define chu printf
#define rint register int
inline ll re()
{
    ll x=0,h=1;char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
const int N=3500+100;
int n,m,p[N];
vector<int>req[N],buc[N];
int ans[N];
pair<int,int>ea[N];
int vis[N],del[N];
multiset<int>ms;
struct edge
{
    int u,v,l,r,id;
}E[N];
struct Picture
{
    struct node
    {
        int to,nxt,id;
    }e[N<<1];
    int tot,head[N],fa[N],pre[N],dep[N];
    inline void add(int x,int y,int bl)
    {
        e[++tot]=(node){y,head[x],bl};head[x]=tot;
    }
    inline void dfs(int x)
    {
        dep[x]=dep[fa[x]]+1;
        for(rint i=head[x];i;i=e[i].nxt)
        {
            int to=e[i].to;
            if(to==fa[x])continue;
            fa[to]=x;
            pre[to]=e[i].id;
            dfs(to);
        }
    }
    inline void lca(int x,int y,int bl)
    {
        if(dep[x]<dep[y])swap(x,y);
        while(dep[x]!=dep[y])
        {
            req[bl].push_back(pre[x]);
            x=fa[x];
        }
        while(x!=y)
        {
            req[bl].push_back(pre[x]);
            req[bl].push_back(pre[y]);
            x=fa[x];y=fa[y];
        }
    }
}dug;
struct Segment
{
    #define lson (rt<<1)
    #define rson (rt<<1|1)
    #define mid ((l+r)>>1)
    int maxi[N<<2],tag[N<<2];
    inline void pushup(int rt)
    {
        maxi[rt]=max(maxi[lson],maxi[rson]);
    }
    inline void update(int rt,int ad)
    {
        maxi[rt]+=ad;tag[rt]+=ad;
    }
    inline void pushdown(int rt)
    {
        if(!tag[rt])return;
        update(lson,tag[rt]);update(rson,tag[rt]);
        tag[rt]=0;
    }
    inline void build(int rt,int l,int r)
    {
        tag[rt]=0;
        if(l==r)
        {
            maxi[rt]=l-1;
            return;
        }
        build(lson,l,mid);build(rson,mid+1,r);
        pushup(rt);
    }
    inline void insert(int rt,int l,int r,int L,int R,int vl)
    {
        //chu("maxi[8]:%d(ins:%d--%d :%d)(%d-%d)\n",maxi[8],L,R,vl,l,r);
        if(L<=l&&r<=R)
        {
            update(rt,vl);return;
        }
        pushdown(rt);
        if(L<=mid)insert(lson,l,mid,L,R,vl);
        if(R>mid)insert(rson,mid+1,r,L,R,vl);
        pushup(rt);
    }
    inline int query(int rt,int l,int r,int L,int R,int std)
    {
        //chu("!!!!!max[%d]:%d std:%d(%d--%d)\n",rt,maxi[rt],std,l,r);
        if(maxi[rt]<std)return -1;
        if(l==r)
        {
            if(maxi[rt]<std)return -1;
            return l;
        }
        pushdown(rt);
        if(R<=mid)return query(lson,l,mid,L,R,std);
        if(maxi[lson]>=std)return query(lson,l,mid,L,R,std);
        return query(rson,mid+1,r,L,R,std);
    }
}seg;
int main()
{
     freopen("mst.in","r",stdin);
    freopen("mst.out","w",stdout);
    // freopen("1.in","r",stdin);
    // freopen("1.out","w",stdout);
    n=re(),m=re();
    for(rint i=1;i<=m;++i)E[i].u=re(),E[i].v=re(),E[i].l=re(),E[i].r=re(),E[i].id=i;
    for(rint i=1;i<n;++i)
    dug.add(E[i].u,E[i].v,E[i].id),dug.add(E[i].v,E[i].u,E[i].id);
    dug.dfs(1);
    for(rint i=n;i<=m;++i)dug.lca(E[i].u,E[i].v,E[i].id);
    for(rint i=n;i<=m;++i)
        for(rint ele:req[i])
        {
           // chu("for:%d smaller should be:%d\n",i,ele);
            E[ele].r=min(E[ele].r,E[i].r-1);
            E[i].l=max(E[i].l,E[ele].l+1);
            buc[ele].push_back(i);
        }
    int ip=1;
    for(rint i=1;i<=m;++i)ea[i]=make_pair(E[i].l,E[i].r);
    sort(ea+1,ea+1+m);
    for(rint i=1;i<=m;++i)
    {
        while(ip<=m&&ea[ip].first<=i)ms.insert(ea[ip].second),ip++;
        while(!ms.empty()&&(*ms.begin())<i)ms.erase(ms.begin());
        if(ms.empty())
        {
            chu("-1");return 0;
        }
        ms.erase(ms.begin());
    }//删除最小的一种构造判断有解
    for(rint i=1;i<=m;++i)p[i]=i;
    sort(p+1,p+1+m,[&](const int & x,const int & y){return E[x].r<E[y].r;});
  //  for(rint k=1;k<=m;++k)chu("seq:(%d-%d)(%d)\n",E[p[k]].l,E[p[k]].r,p[k]);
    for(rint i=1;i<=m;++i)
    {
       // printf("!!!!!!!!!!!!lim{%d}:%d--%d\n",i,E[i].l,E[i].r);
        for(rint j=1;j<=m;++j)vis[j]=0;
        int ip=1;
        seg.build(1,1,m);
      //  chu("maxi[8]:%d\n",seg.maxi[8]);
        for(rint j=1;j<=m;++j)
        {
           // chu("try:%d\n",j);
           // for(rint k=ip;k<=m;++k)chu("seq:(%d-%d)(%d)\n",E[p[k]].l,E[p[k]].r,p[k]);
            if(del[j])seg.insert(1,1,m,1,j,1);//chu("add:%d--%d 1\n",1,j);
            while(ip<=m&&E[p[ip]].r<=j)
            {
                if(E[p[ip]].id>i)seg.insert(1,1,m,1,E[p[ip]].l,1);//chu("dfd add:%d--%d:1(seq:%d)\n",1,E[p[ip]].l,p[ip]);
                ++ip;
            }
            int pos=seg.query(1,1,m,1,j,j);
           // chu("query:%d--%d:%d\n",1,j,pos);
            if(pos!=-1)
            vis[pos]++,vis[j+1]--;//chu("for R:%d the lim:%d--%d\n",j,pos,j);
        }
        for(rint j=1;j<=m;++j)vis[j]+=vis[j-1];
        for(rint j=E[i].l;j<=E[i].r;++j)
        {
            if(!del[j]&&!vis[j])
            {
                ans[i]=j;break;
            }
        }
        del[ans[i]]=1;
        for(rint j:buc[i])E[j].l=max(E[j].l,ans[i]+1);//chu("chg:%d-->%d\n",j,ans[i]+1);
        for(rint j=i+1;j<=m;++j)while(del[E[j].l])E[j].l++;//chu("dfchg:%d-->%d\n",j,E[j].l);
    }
    for(rint i=1;i<=m;++i)chu("%d ",ans[i]);
    return 0;
}
/*
4 5
1 3 1 3
1 4 2 4
1 2 1 4
2 3 3 3
2 3 3 5
int t=x;
x=y;
y=
*/
posted on 2022-11-13 06:29  HZOI-曹蓉  阅读(66)  评论(0编辑  收藏  举报