slove  2/13

rank  252

补题   6/13

----------------------------------------------------------

hdu 6578

http://acm.hdu.edu.cn/showproblem.php?pid=6578

题意:

在给定的n长度的数组中需要填入0-3四个数字,需要满足m个
限制,每个限制的意思是在  区间内只能有x个不同的数字

----------------------------------------

 

四维DP:
每一维表示对0-3四个数字最后一次出现的位置进行排序,第一维就表
示在这四个数中最小的位置,第四维就表示当前位置。
然后因为第四维是滚动的,防止炸内存。

Code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 105;
const ll mod = 998244353;
vector<pair<int, int>> v[N];
ll dp[N][N][N][2];
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        int n, m;
        scanf("%d %d", &n, &m);
        for (int i = 1; i <= n; i++)
            v[i].clear();
        for (int i = 1; i <= m; i++)
        {
            int x, y, z;
            scanf("%d %d %d", &x, &y, &z);
            v[y].push_back(make_pair(x, z));
        }
        memset(dp, 0, sizeof(dp));
        dp[0][0][0][0] = 1;
        for (int now = 1; now <= n; now++)
        {
            for (int i = 0; i <= now; i++)
                for (int j = i; j <= now; j++)
                    for (int k = j; k <= now; k++)
                        dp[i][j][k][now & 1] = 0;
            for (int i = 0; i <= now; i++)
                for (int j = i; j <= now; j++)
                    for (int k = j; k <= now; k++)
                    {
                        (dp[i][j][now - 1][now & 1] += dp[i][j][k][(now & 1) ^ 1]) %= mod;
                        (dp[i][k][now - 1][now & 1] += dp[i][j][k][(now & 1) ^ 1]) %= mod;
                        (dp[j][k][now - 1][now & 1] += dp[i][j][k][(now & 1) ^ 1]) %= mod;
                        (dp[i][j][k][now & 1] += dp[i][j][k][(now & 1) ^ 1]) %= mod;
                    }
            for (int i = 0; i <= now; i++)
                for (int j = i; j <= now; j++)
                    for (int k = j; k <= now; k++)
                    {
                        for (int t = 0; t < v[now].size(); t++)
                        {
                            if ((i >= v[now][t].first) + (j >= v[now][t].first) + (k >= v[now][t].first) + (now >= v[now][t].first) != v[now][t].second)
                                dp[i][j][k][now & 1] = 0;
                        }
                    }
        }
        ll ans = 0;
        for (int i = 0; i <= n; i++)
            for (int j = i; j <= n; j++)
                for (int k = j; k <= n; k++)
                    (ans += dp[i][j][k][n & 1]) %= mod;
        printf("%lld\n", ans);
    }
    return 0;
}

hdu 6579

http://acm.hdu.edu.cn/showproblem.php?pid=6579

题意:有一个整数数列,有2种操作,

0 l r : 表示求区间  任意个数字异或的最大值。

1 x : 表示将 x 加在区间最后面,并且区间长度+1。

-----------------------------------------------

题目要求强制在线,维护区间前缀线性基

Code :

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int wei = 31;
struct LB
{
    int b[wei], cnt;//cnt是个数
    int ind[wei];//ind是下标
    LB()
    {
        cnt = 0;
        memset(ind, -1, sizeof(ind));
        memset(b, 0, sizeof(b));
    }
    bool insert(int x, int id)
    {
        for (int i = wei; i >= 0; i--)
        {
            if (x & (1LL << i))
            {
                if (!b[i])
                {
                    b[i] = x;
                    ind[i] = id;
                    cnt++;
                    return true;
                }
                if (ind[i] < id)
                {
                    swap(ind[i], id);
                    swap(x, b[i]);
                }
                x ^= b[i];
            }
        }
        return false;
    }
};
const ll mod = 1e9 + 7;
int a;
LB lb[1000005];
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        int n, m;
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++)
        {
            lb[i] = lb[i - 1];
            scanf("%d", &a);
            lb[i].insert(a, i);
        }
        ll pre = 0;
        int op, l, r;
        while (m--)
        {
            scanf("%d", &op);
            if (op == 0)
            {
                scanf("%d%d", &l, &r);
                l = (l ^ pre) % n + 1;
                r = (r ^ pre) % n + 1;
                if (l > r)
                    swap(l, r);
                ll res = 0;
                for (int i = wei; i >= 0; i--)
                {
                    if (lb[r].ind[i] >= l && (res ^ lb[r].b[i]) > res)
                        res ^= lb[r].b[i];
                }
                pre = res;
                printf("%lld\n", res);
            }
            else
            {
                scanf("%d", &l);
                l = (l ^ pre);
                lb[n + 1] = lb[n];
                lb[n + 1].insert(l, n + 1);
                n++;
            }
        }
    }
}

hdu 6581

http://acm.hdu.edu.cn/showproblem.php?pid=6581

题意:有n辆车,每辆车有长度,距离,速度,后面的车会被前面的车卡,求自己什么时候能到终点。

--------------------------------------

二分时间,遍历每一辆车,就可以知道某一时间各辆车的位置。

slove by jzk

Code:

#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-10;
struct node
{
    int chang;
    int w;
    int v;
}in[100005];
int n;
bool check(double k)
{
    double dis = in[0].w - in[0].v * k;
    dis += in[0].chang;
    for (int i = 1; i <= n; i++)
    {
        double dis1 = in[i].w - in[i].v * k;
        if (dis1 < dis)
            dis += in[i].chang;
        else
            dis = dis1 + in[i].chang;
    }
    dis -= in[n].chang;
    if (dis <= 0)
        return true;
    return false;
}
int main()
{
    while (scanf("%d", &n) > 0)
    {
        for (int i = n; i >= 0; i--)
            scanf("%d", &in[i].chang);
        for (int i = n; i >= 0; i--)
            scanf("%d", &in[i].w);
        for (int i = n; i >= 0; i--)
            scanf("%d", &in[i].v);
        double l = 0, r = 1000000000;
        while (l + eps < r)
        {
            double k = (l + r) / 2;
            if (check(k))
                r = k;
            else
                l = k;
        }
        printf("%.10lf\n", l);
    }
}

hdu 6582

http://acm.hdu.edu.cn/showproblem.php?pid=6582

题意:使得从点1到点n的最短距离变大的最小花费

----------------------------------------------

dij求出1到其他点的距离,如果一条边的边长=端点到点1的距离之差的绝对值,那么这条边在最短路径上,求出所有在最短路径上的点,跑最小割。

slove by yp

Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAX = 1e5 + 5;
const ll INF = 1e17+9;
struct stu {
    ll dot;
    ll length;
};
bool operator<(stu a, stu b) {
    return a.length > b.length;
}
ll n, m, u[MAX], v[MAX], first[MAX], nextt[MAX], cnt = 0;
ll w[MAX], dis[MAX];
bool vis[MAX];
void add(ll a, ll b, ll c) {
    u[cnt] = a, v[cnt] = b, w[cnt] = c;
    nextt[cnt] = first[u[cnt]];
    first[u[cnt]] = cnt; ++cnt;
}
void dijkstra() {
    dis[1] = 0;
    priority_queue<stu> list1;
    list1.push({ 1,0 });
    while (!list1.empty()) {
        stu now = list1.top(); list1.pop();
        if (vis[now.dot]) continue;
        vis[now.dot] = true;
        for (ll num = first[now.dot]; num != -1; num = nextt[num]) {
            if (dis[v[num]] > dis[u[num]] + w[num]) {
                dis[v[num]] = dis[u[num]] + w[num];
                list1.push({ v[num],dis[v[num]] });
            }
        }
    }
}
ll u1[MAX<<1], v1[MAX<<1], first1[MAX];
ll w1[MAX];
ll nextt1[MAX], cnt1 = 0;
ll deep[MAX];
ll s, t;
ll answer;
ll cur[MAX];
void add1(ll a, ll b, ll c) {
    u1[cnt1] = a, v1[cnt1] = b, w1[cnt1] = c;
    nextt1[cnt1] = first1[u1[cnt1]];
    first1[u1[cnt1]] = cnt1; ++cnt1;
}
bool bfs() {
    deque<ll> list1;
    list1.push_back(s);
    for(ll i=0;i<MAX;++i) deep[i]=INF;
    deep[s] = 0;
    memset(vis, false, sizeof(vis));
    for (ll i = 1; i <= n; ++i) cur[i] = first1[i];
    vis[s] = true;
    while (!list1.empty()) {
        ll now = list1.front(); list1.pop_front();
        for (ll num = first1[now]; num != -1; num = nextt1[num]) {
            if (!vis[v1[num]] && w1[num] > 0) {
                vis[v1[num]] = true;
                deep[v1[num]] = deep[now] + 1;
                list1.push_back(v1[num]);
            }
        }
    }
    return deep[t] != INF;
}
ll dfs(ll now, ll limit) {
    if (!limit || now == t) return limit;
    ll flow = 0, length = 0;
    for (ll num = first1[now]; num != -1; num = nextt1[num]) {
        cur[now] = num;
        if (deep[v1[num]] == deep[now] + 1 && (length = dfs(v1[num], min(limit, w1[num])))) {
            flow += length;
            w1[num] -= length;
            w1[num ^ 1] += length;
            limit -= length;
            if (!limit) break;
        }
    }
    return flow;
}
void dinic() {
    while (bfs()){
        answer += dfs(s, INF);
    }
}

void solve() {
    ll tt;
    scanf("%lld", &tt);
    while (tt--) {
        cnt1 = 0;
        scanf("%lld%lld", &n, &m);
        cnt = 0;
        memset(first, -1, sizeof(first));
        memset(first1, -1, sizeof(first1));
        memset(vis, false, sizeof(vis));
        for(ll i=0;i<MAX;++i) dis[i]=INF;
        memset(cur, 0, sizeof(cur));
        for (ll i = 0; i < m; ++i) {
            ll a, b;
            ll c;
            scanf("%lld%lld%lld", &a, &b, &c);
            add(a, b, c);
        }
        dijkstra();
        if(dis[n]==INF){
            printf("0\n");
            continue;
        }
        for (ll i = 1; i <= n; ++i) 
            for (ll num = first[i]; num != -1; num = nextt[num]) 
                if (dis[v[num]] == dis[u[num]] + w[num]) 
                    add1(u[num], v[num], w[num]), add1(v[num], u[num], 0);
        s = 1, t = n;
        answer = 0;
        dinic();
        printf("%lld\n", answer);
    }
}
int main(void)
{
    solve();
    return 0;
}

hdu 6583

http://acm.hdu.edu.cn/showproblem.php?pid=6583

题意:
 构造一个字符串,在字符串后面加上一个字符需要p代价,在字符串后面加上一个已有的字符子序列需要q代价。
做法:
考虑dp,  首先  就是在第一种直接加字符代价加 p ,第二种的意思是  是  的子序列。 是否是  的子序列就用后缀自动机匹配。

/*这题有个坑点就是一次性初始化会超时,所以就需要用哪个就初始化哪个*/

Code :

#include<bits/stdc++.h>
#define maxc 26
using namespace std;
const int N = 2e5 + 5;
const int mod = 1e9 + 7;
typedef long long ll;
int len[N * 2], //最长子串的长度(该节点字串数量=len[x]-len[link[x]])
link[N * 2],   //后缀链接(最短串前部减少一个字符所到达的状态)
cnt[N * 2],    //被后缀连接的数 
nex[N * 2][maxc],  //状态转移(尾部加一个字符的下一个状态)(图)
idx, //节点编号
last;    //最后节点
ll epos[N * 2]; // enpos数(该状态子串出现数量)
char str[N];
ll a[N];        //长度为i的子串出现最大次数
/*
    匹配字符串
*/
char s[N]; //用以匹配得字符串
int sum[N], ans[N]; //记录匹配字符串每个状态的匹配长度 ans 记录最终每个状态匹配的最短长度
int d[N], id[N];
void newnode(int l) {
    len[idx] = l;
    memset(nex[idx], 0, sizeof(nex[idx]));
}
void init() {    //初始化
    last = idx = 1; //1表示root起始点 空集
    link[1] = len[1] = 0;
    newnode(0);
}
//SAM建图
void insert(int c) {     //插入字符,为字符ascll码值
    int x = ++idx; //创建一个新节点x;
    newnode(len[last] + 1); //  长度等于最后一个节点+1
    epos[x] = 1;  //接受节点子串除后缀连接还需加一
    int p;  //第一个有C转移的节点;
    for (p = last; p && !nex[p][c]; p = link[p])
        nex[p][c] = x;//沿着后缀连接 将所有没有字符c转移的节点直接指向新节点
    if (!p)
        link[x] = 1, cnt[1]++;  //全部都没有c的转移 直接将新节点后缀连接到起点
    else {
        int q = nex[p][c];    //p通过c转移到的节点
        if (len[p] + 1 == len[q])    //pq是连续的
            link[x] = q, cnt[q]++; //将新节点后缀连接指向q即可,q节点的被后缀连接数+1
        else {
            int nq = ++idx;   //不连续 需要复制一份q节点
            len[nq] = len[p] + 1;   //令nq与p连续    
            link[nq] = link[q];   //因后面link[q]改变此处 不加cnt
            memcpy(nex[nq], nex[q], sizeof(nex[q]));  //复制q的信息给nq
            for (; p && nex[p][c] == q; p = link[p])
                nex[p][c] = nq;    //沿着后缀连接 将所有通过c转移为q的改为nq
            link[q] = link[x] = nq; //将x和q后缀连接改为nq
            cnt[nq] += 2; //  nq增加两个后缀连接
        }
    }
    last = x;  //更新最后处理的节点
}
void GetNpos() {    //求npos数,即该节点字串出现次数
    queue<int>q;
    for (int i = 1; i <= idx; i++)
        if (!cnt[i])
            q.push(i);   //将所有没被后缀连接指向的节点入队
    while (!q.empty()) {
        int x = q.front();
        q.pop();
        epos[link[x]] += epos[x]; //子串数量等于所有后缀连接指向该节点的子串数量和+是否为接受节点
        if (--cnt[link[x]] == 0)
            q.push(link[x]);   //当所有后缀连接指向该节点的处理完毕后再入队
    }
}
void GetSubNum() {    //求不相同字串数量
    ll ans = 0;
    for (int i = 2; i <= idx; i++)
        ans += len[i] - len[link[i]];    //一状态子串数量等于len[i]-len[link[i]]
    printf("%lld\n", ans);
}
void GetSubMax() {    //求出所有长度为k的子串中出现次数最多的子串出现次数
    GetNpos();
    int n = strlen(str);
    for (int i = 1; i <= idx; i++)
        a[len[i]] = max(a[len[i]], epos[i]);    //长度≤k的子串中出现次数最多的子串出现次数的最小值
    for (int i = n - 1; i >= 1; i--)
        a[i] = max(a[i], a[i + 1]);        //求一遍后缀最大值就是答案
    for (int i = 1; i <= n; i++)
        printf("%lld\n", a[i]);
}
void Match()//匹配字符串得最长公共子串
{
    for (int i = 1; i <= idx; i++)
        ans[i] = len[i]; //初始匹配长度为本身字符串的子串长度
    for (int i = 1; i <= idx; i++)
        d[len[i]]++; 
    for (int i = 1; i <= idx; i++)
        d[i] += d[i - 1];
    for (int i = 1; i <= idx; i++) 
        id[d[len[i]]--] = i;  /**/
    while (~scanf("%s", s))
    {
        int n = strlen(s);
        int num = 0, p = 1;// p代表节点 num 匹配长度
        memset(sum, 0, sizeof(sum));
        for (int i = 0; i < n; i++)
        {
            int c = s[i] - 'a';
            if (nex[p][c])
                num++, p = nex[p][c];
            else
            {
                for (; p && !nex[p][c]; p = link[p]);
                if (p)
                    num = len[p] + 1, p = nex[p][c];
                else
                    num = 0, p = 1;
            }
            sum[p] = max(sum[p], num); /* 节点匹配最大长度*/
        }
        for (int i = idx; i >= 1; i--)/*与后缀链接相比*/
            sum[link[id[i]]] = max(sum[link[id[i]]], sum[id[i]]);
        for (int i = 1; i <= idx; i++)/**/
            ans[i] = min(ans[i], sum[i]);
    }
    int Ans = 0; /**/
    for (int i = 1; i <= idx; i++)
        Ans = max(Ans, ans[i]);
    printf("%d", Ans);
}
ll dp[N];
int main() {
    int p, q;
    while (~scanf("%s", str + 1))
    {
        init();
        int n = strlen(str + 1);
        scanf("%d %d", &p, &q);
        int cur = 1, now = 1, pos = 1; /*cur 表示当前位置 now 表示 从now - cur的位置是匹配的*/
        while (cur <= n)
        {
            int x = str[cur] - 'a';
            if (nex[pos][x]) /*可以匹配*/
            {
                pos = nex[pos][x];
                dp[cur] = min(dp[now - 1] + q, dp[cur - 1] + p);
                cur++;
            }
            else if (cur == now) /*直接加入now位置的字符*/
            {
                insert(x);
                dp[cur] = dp[cur - 1] + p;
                cur++;
                now++;
            }
            else/*如果前面的情况都不存在就需要把now位置字符加入并且调整开始匹配的位置*/
            {
                insert(str[now++] - 'a');
                int tlen = cur - now;
                for (; pos && link[pos] && len[link[pos]] >= tlen; pos = link[pos]);

            }
        }
        printf("%lld\n", dp[n]);
    }
}
/*
abcbc
*/

hdu 6586 

http://acm.hdu.edu.cn/showproblem.php?pid=6586

题意:给你一个字符串,再给一个整数k,然后26行li,ri表示26个字母的限制范围,让你从给出的字符串中
找出字典序最小的k长度的字符串

------------------------------------------------
思路:贪心的搜,每次都选择符合条件的最小字母,搜满k个为止,
判断依据为选完当前位置后,剩下的字符串是否满足条件 

Code :

#include<bits/stdc++.h>
using namespace std;
const int MAX = 1e5+5;
char s[MAX],ans[MAX];
int n,l[30],r[30],len,used[30];
int index[30][MAX],endd[30],now[30],last,countt[MAX][30]; 
bool check=true;
void solve(){
    while(~scanf("%s%d",s,&n)){
        check=true;
        last=-1;
        memset(used,0,sizeof(used));
        for(int i=0;i<26;++i) scanf("%d%d",&l[i],&r[i]);
        memset(endd,-1,sizeof(endd));
        memset(now,0,sizeof(now));
        memset(countt,0,sizeof(countt));
        len=strlen(s);
        for(int i=0;i<len;++i){
            int t = s[i]-'a';
            index[t][++endd[t]]=i;
        }
        for(int i=len-1;i>=0;--i)
            for(int j=0;j<26;++j)
                countt[i][j]=countt[i+1][j]+(s[i]=='a'+j);
        for(int i=0;i<n;++i){
            bool low=false;
            for(int j=0;j<26;++j){
                bool flag=true;
                //当前字母已经用到了上限 
                if(used[j]==r[j]) continue;
                //取符合条件的那一个字母 
                while(now[j]<=endd[j]&&index[j][now[j]]<=last) ++now[j];
                //当前字母已经用到了最后一个 
                if(now[j]>endd[j]) continue;
                ++used[j];
                //check后面的字母是否能满足l,r数组
                int tem=index[j][now[j]],sum=0;
                for(int k=0;k<26;++k){
                    if(countt[tem+1][k]+used[k]<l[k]){
                        flag=0;
                        break;
                    }
                    sum+=max(l[k]-used[k],0);
                }
                if(sum>n-i-1) flag=0;
                //check需要的字母是否能构成n长度字符串
                sum=0;
                for(int k=0;k<26;++k)
                    sum+=min(countt[tem+1][k],r[k]-used[k]);
                if(sum<n-i-1) flag=0;
                if(flag){
                    ans[i]='a'+j;
                    ans[i+1]='\0';
                    low=true;
                    last=tem;
                    break;
                }
                else --used[j];
            }
            if(!low){
                check=false;
                break;
            }
        }
        if(check)printf("%s\n",ans);
        else printf("-1\n");
    }    
}
int main(void)
{
    solve();
    return 0;
}