T1[状态压缩DP]给出\(n,m,p,q,r\),求长度是n,值域在\([1,m]\)之间的序列个数,满足\(\exists 1\leq x<y<z<k\leq n,\)\(sum(x,y-1)=p,sum(y,z-1)=q,sum(z,k)=r\).(n<=50,max(p,q,r)<=6)

考场

想到数位DP,依次确定每一位填什么,维护到目前位数的连续段,\(bit,now,lef\),表示填到a的第bit位,连续的pqr已经填到了now,now位还差lef个数填满,每次枚举填什么,\([1,lef]\)填可以有进位1的贡献,\([lef+1,v[1]]\)有从1置位的贡献
\(max(v[1],m)-m\)是置0的贡献,只统计合法位置第一次出现,剩下的就是一个次方。
结果它总是少算啊,怎么搞都少,没辙了,交了暴力10pts

正解

问题是什么?是你在x位置匹配不合法不一定会回退到1位置,可能是回退到某个位继续贡献(就是你强制的到目前不合法不管用了,可能填着填着合法了但是你不知道,于是就算少了),这样就不能只保留最后一位信息,需要保留更多。多到多少呢?发现pq,r很小,那么合法的一段缀应该是长度<=18的,而且\(s1+s2+s3=p+q+r,s1=p,s2=q,s3=r\),一看就不多,打表发现最多有10万多个,于是就考虑直接计算出这些状态然后转移
\(t[S][j]:表示目前状态是S,如果在后面再接一个数j,序列会变成的序列编号。\)
dfs进行枚举每次填什么,在这个基础上枚举再填一位会发生什么变化,如果\(islegal\),状态设置-1,
否则更新map新状态加入。注意维护的是一段后缀,所以每个状态只保留\(<sum\)的部分,=sum的部分作为不合法状态已经设置-1了。
DP就是模拟填数过程
\(O(n*(min(m,6))*state_{cnt})\)
%%%APjifengc

点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define ll long long
#define ull unsigned long long
#define chu printf
inline ll re() {
    ll x = 0, h = 1;
    char ch = getchar();
    while (ch > '9' || ch < '0') {
        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 = 2e5 + 100;
const int mod = 998244353;
int n, m, p, q, r;
int f[2][N], t[N][8];
int cnt, pw[N];
struct State {
    deque<int> q;
    int sum;
    void push(int x) {
        q.push_front(x);
        sum += x;
    }
    void pop() {
        sum -= q.back();
        q.pop_back();
    }
};
struct Hash {
    int hs1, hs2;
    void clear() { hs1 = hs2 = 0; }
    void operator+=(int A) {
        hs1 = (1ll * hs1 * 13331 + A) % 993244853;
        hs2 = (1ll * hs2 * 131 + A) % 1000000009;
    }
    bool operator==(const Hash& A) const { return hs1 == A.hs1 && hs2 == A.hs2; }
    size_t operator()(const Hash& A) const { return A.hs1 ^ A.hs2; }
};
unordered_map<Hash, int, Hash> mp;
inline Hash getHash(State& s) {
    Hash hsh;
    hsh.clear();
    for (rint ele : s.q) hsh += ele;
    return hsh;
}
State tt;
inline void dfs(int s) {
    if (s >= p + q + r)
        return;
    // printf("s;%d\n",s);
    Hash now = getHash(tt);
    if (!mp.count(now))
        mp[now] = ++cnt;
    int nid = mp[now];
    for (rint i = 1; i <= min(6, m); ++i) {
        State ls = tt;
        ls.push(i);
        while (ls.sum > p + q + r) ls.pop();
        if (ls.sum == p + q + r) {
            //  printf("%d equal\n",i);
            int ar = 0, br = 0, cr = 0;
            for (rint j : ls.q) {
                if (ar != p) {
                    ar += j;
                    if (ar > p)
                        break;
                } else if (br != q) {
                    br += j;
                    if (br > q)
                        break;
                } else if (cr != r) {
                    cr += j;
                    if (cr > r)
                        break;
                }
            }
            if (ar == p && br == q && cr == r) {
                // printf("%d %d no\n",nid,i);
                t[nid][i] = -1;
                continue;
            }
        }
        while (ls.sum >= p + q + r) ls.pop();
        Hash uu = getHash(ls);
        if (!mp.count(uu))
            mp[uu] = ++cnt;
        int newid = mp[uu];
        t[nid][i] = newid;
    }
    for (rint i = 1; i <= min(6, m); ++i) {
        State orz;
        orz = tt;
        tt.push(i);
        dfs(s + i);
        tt = orz;
    }
}
int main() {
    // freopen("1.in","r",stdin);
    // freopen("2.out","w",stdout);
    freopen("a.in", "r", stdin);
    freopen("a.out", "w", stdout);
    n = re(), m = re(), p = re(), q = re(), r = re();
    mp[(Hash){ 0, 0 }] = 0;
    dfs(0);
    pw[0] = 1;
    for (rint i = 1; i <= n; ++i) pw[i] = 1ll * pw[i - 1] * m % mod;
    int o = 0;
    f[o][0] = 1;
    int ans = 0;
    // chu("%d\n",cnt);int rr=0;
    // for(rint i=1;i<=cnt;++i)
    //    for(rint j=1;j<=4;++j)if(t[i][j]==-1)++rr;
    // chu("r:%d\n",rr);
    for (rint i = 1; i <= n; ++i, o ^= 1) {
        memset(f[o ^ 1], 0, sizeof(f[o ^ 1]));
        for (rint j = 0; j <= cnt; ++j) {
            if (f[o][j]) {
                int v = f[o][j];
                for (rint k = 1; k <= min(m, 6); ++k) {
                    int rel = t[j][k];
                    if (rel == -1) {
                        ans = (ans + 1ll * v * pw[n - i] % mod) % mod;
                        continue;
                    }
                    f[o ^ 1][rel] = (f[o ^ 1][rel] + v) % mod;
                }
                if (m > 6) {
                    f[o ^ 1][0] = (f[o ^ 1][0] + 1ll * v * (m - 6) % mod) % mod;
                }
            }
        }
    }
    chu("%d", ans);
    return 0;
}
/*
10 5 3 5 4
*/

T3[图论/贪心]n个点的无向有权图,可以任意次交换边权,求\((u,v)(v,w)\)最短路之和。(n<=2e5)

考场

这不就是分类讨论一下嘛,边权取前k小sort一下呗。
最直接想到\(u->v\)记录一个1为边权的最短路,然后v再计算一遍到w,发现路径可以重复,于是跑DIJ记录最短路劲上的点,枚举重复的点O(1)计算贡献。

正解

差一步,差什么呢?
因为你不一定是要跑u,v最短路径上的点,任意点都可以成为决策!
还有就是下标炸了。

点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define ll long long
#define ull unsigned long long
#define chu printf
inline ll re() {
    ll x = 0, h = 1;
    char ch = getchar();
    while (ch > '9' || ch < '0') {
        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 = 2e5 + 100;
int n, m, u, v, w;
int c[N], tot = 1;
ll sum[N];
int dis[3][N], head[N];
int ind[N], Ind;
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;
bool vis[N];
struct node {
    int to, nxt;
} e[N << 1];
inline void add_e(int x, int y) {
    e[++tot] = (node){ y, head[x] };
    head[x] = tot;
}
inline void dij(int st, int opt) {
    memset(dis[opt], 0x3f, sizeof(dis[opt]));
    dis[opt][st] = 0;
    fill(vis + 1, vis + 1 + n, 0);
    q.push(make_pair(0, st));
    while (!q.empty()) {
        int x = q.top().second;
        q.pop();
        if (vis[x])
            continue;
        vis[x] = 1;
        for (rint i = head[x]; i; i = e[i].nxt) {
            int to = e[i].to;
            if (dis[opt][to] > dis[opt][x] + 1) {
                dis[opt][to] = dis[opt][x] + 1;
                q.push(make_pair(dis[opt][to], to));
            }
        }
    }
}
int main() {
    // freopen("c3.in","r",stdin);
    // freopen("1.out","w",stdout);
    freopen("c.in", "r", stdin);
    freopen("c.out", "w", stdout);
    n = re(), m = re(), u = re(), v = re(), w = re();
    for (rint i = 1; i <= m; ++i) {
        int ai = re(), bi = re(), ci = re();
        add_e(ai, bi);
        add_e(bi, ai);
        c[i] = ci;
    }
    sort(c + 1, c + 1 + m);
    for (rint i = 1; i <= m; ++i) sum[i] = sum[i - 1] + c[i];
    dij(u, 0);
    dij(v, 1);
    dij(w, 2);
    for (rint i = 1; i <= n; ++i) ind[++Ind] = i;
    //   for(rint i=1;i<=n;++i)chu("u:disto:%d is :%d\n",i,dis[1][i]);
    //  for(rint i=1;i<=Ind;++i)chu("ind[%d]:%d\n",i,ind[i]);
    ll ans = 1e18;
    for (rint i = 1; i <= Ind; ++i)  //枚举从u->v,从v去到w路径上的重合部分
    {
        ll nowans = 0;
        int cut = dis[1][ind[i]];
        int d1 = dis[0][ind[i]], d2 = dis[2][ind[i]];
        int d = d1 + d2;
        if (d1 + d2 + cut > m)
            continue;
        nowans = sum[cut + d] + sum[cut], ans = min(ans, nowans);
    }
    chu("%lld", ans);
    return 0;
}
/*
边权赋值1,求最短路
dis[u][]
dis[v][]
dis[w][]
把u,v上的最短路径拿出来,枚举断点
*/

T4[图论/欧拉路]给你n条线段(x1,y1)(x2,y2),保证水平或者竖直,要你求最少多少次落笔,覆盖这些线段K次。(K,n<=2e3,x,y<=1e6)

考场

手摸一下发现就是欧拉路问题,覆盖线段K次看成有K条边,要求走完这些边的最少落笔次数,考虑奇数点成对出现,每次落笔只能最多消灭一对奇数点,而且矛盾点只在奇数点,(考虑多一条边消灭奇数点,那么偶数点就是欧拉回路),所以对于一个连通图,下笔次数是\(max(1,cnt/2)\),cnt是奇数点个数。如果不连通,就是多一个联通快多一次落笔,+1就行。注意第一次落笔不算要-1.
对,这些都是考场的想法,然后开始打,发现把线段转化点非常恶心,开始枚举线段横竖\(n^2\),枚举寻找左边第一个小于,上边第一个小于,端点又是4个大循环畅快讨论,200多行过去了,1个半小时过去了,发现连图都没建呢,复杂度也假了,果断放弃。

正解

首先结论继承上面的,然后大谈建点:
先把点拿出来,具体的,端点加入,然后交点加入,去重?这种事情不要一堆ifelse,直接map上
然后考虑连边,发现无论是枚举线段交点找点,还是枚举点找4个方向都麻烦得不行,考虑直接枚举线段对点的贡献,直接横着竖着扫一遍每条线段联通的一条线上的点,然后再考虑横竖之间的…等等!?没了?这就是全部?神奇!
\(O(n^2logn)\)

点击查看代码
// ubsan: undefined
// accoders
//鹤都鹤T了我也是够了,找不同功底日益深厚...
#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define ll long long
#define ull unsigned long long
#define chu printf
inline ll re() {
    ll x = 0, h = 1;
    char ch = getchar();
    while (ch > '9' || ch < '0') {
        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 = 4100;
int n, m, cnt;
map<pair<int, int>, bool> mp2;  //对点对进行映射去重
struct edge {
    int to, nxt;
} e[N * N * 4];
int head[N * N], tot;
vector<pair<int, int> > ph[N], ps[N],
    tmp;  //存横坐标或者纵坐标意义下的线段对,是为了建立点,也方便到时候对边找贡献
//还要搞一个tmp,因为重复的线段需要处理
//点也要放进去,分横和纵的
int mpx[N << 1], mpy[N << 1], mmx, mmy;
vector<int> dh[N], ds[N];
struct linshi {
    int a1, b1, a2, b2;
} lsp[N];
int deg[N * N];
bool vis[N * N];
vector<int> vec;
inline void add(int x, int y) {
    e[++tot] = (edge){ y, head[x] };
    head[x] = tot;
}
inline void add_e(int x, int y) {
    if (mp2.count(make_pair(x, y)) == 0) {
        // chu("%d--%d\n",x,y);
        add(x, y);
        add(y, x);
        mp2[make_pair(x, y)] = 1;
    }
}
int mp[4000 + 10][4000 + 10];
inline void insert(int x, int y) {
    // chu("insert:%d %d\n",x,y);
    if (mp[x][y] == 0)
        mp[x][y] = ++cnt, dh[x].push_back(y), ds[y].push_back(x);  // chu("succode\n");//加入点
    // else chu("NO\n");
}
inline bool Jiao(int x, pair<int, int> xl, int y, pair<int, int> yl) {
    if (x <= yl.second && x >= yl.first && y <= xl.second && y >= xl.first)
        return 1;
    return 0;
}
inline void dfs(int x) {
    vec.push_back(x);
    vis[x] = 1;
    for (rint i = head[x]; i; i = e[i].nxt) {
        ++deg[x];  //就是度数
        int to = e[i].to;
        if (vis[to])
            continue;
        dfs(to);
    }
}
int use[N], use2[N];
int main() {
    freopen("d.in", "r", stdin);
    freopen("d.out", "w", stdout);
    // freopen("d.in","r",stdin);
    // freopen("d.out","w",stdout);
    m = re(), n = re();
    for (rint i = 1; i <= m; ++i) {
        lsp[i].a1 = re(), lsp[i].b1 = re(), lsp[i].a2 = re(), lsp[i].b2 = re();
        mpx[++mmx] = lsp[i].a1;
        mpx[++mmx] = lsp[i].a2;
        mpy[++mmy] = lsp[i].b1;
        mpy[++mmy] = lsp[i].b2;
    }
    sort(mpx + 1, mpx + 1 + mmx);
    sort(mpy + 1, mpy + 1 + mmy);
    mmx = unique(mpx + 1, mpx + 1 + mmx) - mpx - 1;
    mmy = unique(mpy + 1, mpy + 1 + mmy) - mpy - 1;
    for (rint i = 1; i <= m; ++i) {
        lsp[i].a1 = lower_bound(mpx + 1, mpx + 1 + mmx, lsp[i].a1) - mpx;
        lsp[i].b1 = lower_bound(mpy + 1, mpy + 1 + mmy, lsp[i].b1) - mpy;
        lsp[i].a2 = lower_bound(mpx + 1, mpx + 1 + mmx, lsp[i].a2) - mpx;
        lsp[i].b2 = lower_bound(mpy + 1, mpy + 1 + mmy, lsp[i].b2) - mpy;
        if (lsp[i].a1 > lsp[i].a2)
            swap(lsp[i].a1, lsp[i].a2);
        if (lsp[i].b1 > lsp[i].b2)
            swap(lsp[i].b1, lsp[i].b2);
        // chu("%d %d %d %d\n",lsp[i].a1,lsp[i].b1,lsp[i].a2,lsp[i].b2);
        if (lsp[i].a1 == lsp[i].a2) {
            ph[lsp[i].a1].push_back(make_pair(lsp[i].b1, lsp[i].b2));
        } else {
            ps[lsp[i].b1].push_back(make_pair(lsp[i].a1, lsp[i].a2));
        }
    }
    //  return 0;
    // chu("dfsrewr\n");
    for (rint i = 1; i <= mmx; ++i) {
        sort(ph[i].begin(), ph[i].end());
        int sz = ph[i].size();
        tmp.clear();
        int rem = -1;
        for (rint j = 0; j < sz; ++j) {
            if (rem == -1 || ph[i][j].first > ph[i][rem].second) {
                if (rem != -1)
                    tmp.push_back(ph[i][rem]);
                rem = j;
            } else {
                ph[i][rem].second = max(ph[i][rem].second, ph[i][j].second);
            }
        }
        if (rem != -1)
            tmp.push_back(ph[i][rem]);
        ph[i] = tmp;
    }
    // chu("dfs\n");
    for (rint i = 1; i <= mmy; ++i) {
        sort(ps[i].begin(), ps[i].end());
        int sz = ps[i].size();
        tmp.clear();
        int rem = -1;
        for (rint j = 0; j < sz; ++j) {
            if (rem == -1 || ps[i][j].first > ps[i][rem].second) {
                if (rem != -1)
                    tmp.push_back(ps[i][rem]);
                rem = j;
            } else {
                ps[i][rem].second = max(ps[i][rem].second, ps[i][j].second);
            }
        }
        if (rem != -1)
            tmp.push_back(ps[i][rem]);
        ps[i] = tmp;
    }
    // for(rint i=1;i<=mmy;++i)
    // {
    //     for(auto j:ps[i])chu("for:%d is %d %d\n",i,j.first,j.second);
    // }
    //  chu("dfsdf\n");
    for (rint i = 1; i <= mmx; ++i)
        if (ph[i].size())
            use[++use[0]] = i;
    for (rint i = 1; i <= mmy; ++i)
        if (ps[i].size())
            use2[++use2[0]] = i;
    for (rint I = 1; I <= use[0]; ++I) {
        int i = use[I];
        // chu("i:%d(%d)\n",i,ph[i].size());
        for (pair<int, int> j : ph[i])  //遍历线段
        {
            //   chu("in?\n");
            insert(i, j.first);
            insert(i, j.second);
            for (rint II = 1; II <= use2[0]; ++II) {
                int ii = use2[II];
                // chu("ii:%d(%d)\n",ii,ps[ii].size());
                for (pair<int, int> jj : ps[ii])  //找交点,啊,要T的感觉
                {
                    insert(jj.first, ii);
                    insert(jj.second, ii);
                    //    chu("wher slow:%d\n",jj.first);
                    if (Jiao(i, j, ii, jj))  //有交点
                    {
                        insert(i, ii);
                    }
                }
            }
        }
    }
    //  chu("dfdsfds\n");
    for (rint I = 1, i = use[I]; I <= use[0]; ++I, i = use[I]) {
        sort(dh[i].begin(), dh[i].end());
        //  for(rint j:dh[i])chu("dot:(%d %d)(id:%d)\n",i,j,mp[(node){i,j}]);
    }
    // chu("dfsdfdssdf\n");
    for (rint I = 1, i = use2[I]; I <= use2[0]; ++I, i = use2[I]) {
        sort(ds[i].begin(), ds[i].end());
        // chu("siz[%d]:%ld\n",i,ds[i].size());
        //  for(rint j:ds[i])chu("sdot:(%d %d)\n",j,i);
    }
    // return 0;
    //点有了,编号也有了,然后就是用线段连
    //遍历线段
    //  chu("he is out\n");
    for (rint I = 1, i = use[I]; I <= use[0]; ++I, i = use[I]) {
        //是横着的
        for (pair<int, int> j : ph[i])  //找到它的端点位置
        {
            int p1 = lower_bound(dh[i].begin(), dh[i].end(), j.first) - dh[i].begin();
            int p2 = lower_bound(dh[i].begin(), dh[i].end(), j.second) - dh[i].begin();
            // p1--p2连接
            for (rint k = p1; k < p2; ++k) {
                add_e(mp[i][dh[i][k]], mp[i][dh[i][k + 1]]);
            }
        }
    }
    //  chu("dfdsfsdfdsfdsfdsf\n");
    for (rint I = 1, i = use2[I]; I <= use2[0]; ++I, i = use2[I]) {
        //是横着的
        for (pair<int, int> j : ps[i])  //找到它的端点位置
        {
            int p1 = lower_bound(ds[i].begin(), ds[i].end(), j.first) - ds[i].begin();
            int p2 = lower_bound(ds[i].begin(), ds[i].end(), j.second) - ds[i].begin();
            // p1--p2连接
            for (rint k = p1; k < p2; ++k) {
                add_e(mp[ds[i][k]][i], mp[ds[i][k + 1]][i]);
            }
        }
    }
    //然后边就连完了,跑图?
    int ans = -1;
    for (rint i = 1; i <= cnt; ++i)
        if (!vis[i]) {
            ++ans;  //一个独立图+1
            dfs(i);
            if (n & 1)  //是奇数
            {
                int sad = 0;
                for (rint j : vec)
                    if (deg[j] & 1)
                        ++sad;  // chu("%d is in(%d)\n",j,deg[j]);
                ans += max(0, sad / 2 - 1);
                // chu("start is:%d the sad:%d\n",i,sad);
                // chu("now ans:%d\n",ans);
                vec.clear();
            }
            //是偶数就不管了
        }
    chu("%d", ans);
    return 0;
}
/*
4 1
1 1 1 0
1 1 0 1
1 1 1 2
1 0 1 2

枚举每个线段,枚举方向不一样的线段,统计交点。
考虑对于(x,y):
[1]上面有边:存在上面的线段在子线段中,包含了y
[2]右边有边:存在右边的线段在子线段中,包含了x
所以init_deg=0--4
然后*m,判断奇数偶数直接讨论了
但是如果m是偶数,直接就是偶数,还要算个寄
开始猜结论
通过一些途径统计出点,然后连边
统计度数是奇数的点=cnt
if(cnt<=2)ans=1;
else ans=cnt/2
记得-1!
不同联通块,+=code-1
*/

原文地址:http://www.cnblogs.com/403caorong/p/16900907.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性