【2019 HDU多校集训第一场】

1.首先,总的来说HDU多校这次难度的是有的,但是并非表现得那么难,可能有很多队伍没有发挥好。

2.1005 Path.这个题目出了好多次了,基本套路,扣最短路跑最大流。

后记:有大佬说代码有些冗余,不用传dis数组,其实无所谓的,因为DJ哪个函数里已经每次把dis每次给重新赋值了。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll INF = 1e18;
const ll N = 10000 + 10;
const ll MAXM = 100000 + 10;

ll n, m;

struct Node
{
	ll from, to, next;
	ll cap;
} edge[MAXM];

ll tol;
ll head[N];
ll dep[N];
ll gap[N];

void init()
{
	tol = 0;
	memset(head, -1, sizeof(head));
}

void addedge(ll u, ll v, ll w)
{
	//cout<<u<<" "<<v<<" "<<w<<endl;

	edge[tol].from = u;
	edge[tol].to = v;
	edge[tol].cap = w;
	edge[tol].next = head[u];
	head[u] = tol++;
	edge[tol].from = v;
	edge[tol].to = u;
	edge[tol].cap = 0;
	edge[tol].next = head[v];
	head[v] = tol++;
}
void BFS(ll start, ll end)
{
	memset(dep, -1, sizeof(dep));
	memset(gap, 0, sizeof(gap));
	gap[0] = 1;
	ll que[N];
	ll front, rear;
	front = rear = 0;
	dep[end] = 0;
	que[rear++] = end;
	while (front != rear)
	{
		ll u = que[front++];
		if (front == N)
			front = 0;
		for (ll i = head[u]; i != -1; i = edge[i].next)
		{
			ll v = edge[i].to;
			if (dep[v] != -1)
				continue;
			que[rear++] = v;
			if (rear == N)
				rear = 0;
			dep[v] = dep[u] + 1;
			++gap[dep[v]];
		}
	}
}

ll SAP(ll start, ll end)
{
	ll res = 0;
	BFS(start, end);
	ll cur[N];
	ll S[N];
	ll top = 0;
	memcpy(cur, head, sizeof(head));
	ll u = start;
	ll i;
	while (dep[start] < n)
	{
		if (u == end)
		{
			ll temp = INF;
			ll inser;
			for (i = 0; i < top; i++)
				if (temp > edge[S[i]].cap)
				{
					temp = edge[S[i]].cap;
					inser = i;
				}
			for (i = 0; i < top; i++)
			{
				edge[S[i]].cap -= temp;
				edge[S[i] ^ 1].cap += temp;
			}
			res += temp;
			top = inser;
			u = edge[S[top]].from;
		}
		if (u != end && gap[dep[u] - 1] == 0) //出现断层,无增广路
			break;
		for (i = cur[u]; i != -1; i = edge[i].next)
			if (edge[i].cap != 0 && dep[u] == dep[edge[i].to] + 1)
				break;
		if (i != -1)
		{
			cur[u] = i;
			S[top++] = i;
			u = edge[i].to;
		}
		else
		{
			ll min = n;
			for (i = head[u]; i != -1; i = edge[i].next)
			{
				if (edge[i].cap == 0)
					continue;
				if (min > dep[edge[i].to])
				{
					min = dep[edge[i].to];
					cur[u] = i;
				}
			}
			--gap[dep[u]];
			dep[u] = min + 1;
			++gap[dep[u]];
			if (u != start)
				u = edge[S[--top]].from;
		}
	}
	return res;
}

struct Edge
{
	ll v, w;
	Edge(ll v, ll w) : v(v), w(w) {}
};

vector<Edge> G[N];
ll dis[N], cnt[N];
bool vis[N];

void Dijkstra(ll s)
{
	priority_queue<pair<ll, ll> > Q;

	for (ll i = 0; i < n; i++)
		dis[i] = INF, cnt[i] = INF;
	memset(vis, 0, sizeof(vis));

	Q.push(make_pair(0, s));
	dis[s] = 0;
	cnt[s] = 0;

	while (!Q.empty())
	{
		ll u = Q.top().second;
		Q.pop();
		if (vis[u])
			continue;
		vis[u] = 1;
		for (ll i = 0; i < G[u].size(); i++)
		{
			Edge& e = G[u][i];
			if (dis[u] + e.w < dis[e.v])
			{
				dis[e.v] = dis[u] + e.w;
				Q.push(make_pair(-dis[e.v], e.v));
			}
		}
	}
}

vector<ll> val[N];

int main()
{

	ll tt;
	scanf("%d", &tt);
	while (tt--)
	{
		scanf("%lld%lld", &n, &m);
		ll x, y, t;
		for (ll i = 0; i < n; i++)
		{
			G[i].clear();
			val[i].clear();
		}
		for (ll i = 0; i < m; i++)
		{
			scanf("%lld%lld%lld", &x, &y, &t);
			x--, y--;
			G[x].push_back(Edge(y, t));
			val[x].push_back(t);
		}
		Dijkstra(0);
		if (dis[n - 1] == INF)
		{
			cout << 0 << endl;
			return 0;
		}

		init();
		for (ll u = 0; u < n; u++)
		{
			for (ll i = 0; i < G[u].size(); i++)
			{
				ll v = G[u][i].v, w = val[u][i];
				if (dis[u] + w == dis[v])
				{
					addedge(u, v, w);
				}
			}
		}

		printf("%lld\n", SAP(0, n - 1));
	}
	return 0;
}

3.1004.Vacation。其实是个思维题目,只需要枚举一下那一辆车是最慢的,他会挡住后边的所有车,然后所有吃的速度就会和他的速度一样。20行代码即可解决。开始想了一个二分时间的写法,但是没有仔细读题,我以为一辆车通过之后对其他车不再有影响,实际上这个影响一直存在。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 100005
ll n, l[maxn], s[maxn], v[maxn], sum[maxn];
int main()
{
    ios::sync_with_stdio(false);
    while (cin >> n)
    {
        ++n;
        for (int i = n; i >= 1; i--)cin >> l[i];
        for (int i = n; i >= 1; i--)cin >> s[i];
        for (int i = n; i >= 1; i--)cin >> v[i];
        for (int i = 1; i <= n; i++)sum[i] = sum[i - 1] + l[i];
        double ans = 1.0 * s[n] / v[n];
        for (int i = 1; i < n; i++)ans = max(ans, 1.0 * (s[i] + sum[n - 1] - sum[i - 1] )/ v[i]);
        cout << fixed;
        cout << setprecision(7) << ans << endl;
    }
}

4.1002.Operation这个题目实在是让人自闭,其实很简单,但是当时上来先入为主,写了线段树维护区间线性基的写法,结果时间被卡。其实这个题目加一点思维就好了,由于题目每次从后插入数据,那么我们构造对于每一个前缀构造线性基的时,优先从后边插入线性基,这样做一个f[maxn][32]的数组,f[i][j]表示第i个前缀的第j个基的下标,对于每一个询问,我们只需要在这个前缀里面找符合条件的基。然后把他们插入一个新的线性基求最值即可。插入值之后的线性基,我们可以采用前一个线性基构造。时间复杂度O(32*N).

#include<bits/stdc++.h>
using namespace std;

//究极读入挂
inline char nc() {
    static char buf[100000], * p1 = buf, * p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
inline int _read() {
    char ch = nc(); int sum = 0;
    while (!(ch >= '0' && ch <= '9'))ch = nc();
    while (ch >= '0' && ch <= '9')sum = sum * 10 + ch - 48, ch = nc();
    return sum;
}


const int maxn = 1e6 + 6;

struct l_b {
    static const int maxbit = 32;
    int b[maxbit];

    void ini() {
        memset(b, 0, sizeof(b));
    }

    bool insert(int x) {
        for (int i = maxbit - 1; i >= 0; i--)
            if (x & (1 << i)) {
                if (!b[i]) {
                    b[i] = x;
                    break;
                }
                x ^= b[i];
            }
        return x > 0;
    }

    int Max(int x) {
        int res = x;
        for (int i = maxbit - 1; i >= 0; i--)
            res = max(res, res ^ b[i]);
        return res;
    }

} lb;

int base[maxn][32];
int a[maxn];


//base[maxn][j]前缀i的第j个基的下标
void add(int i) 
{
    lb.ini();
    base[i][0] = 0;
    lb.insert(a[i]);
    base[i][++base[i][0]] = i;
    for (int j = 1; j <= base[i - 1][0]; j++) 
    {
        if (lb.insert(a[base[i - 1][j]])) 
        {
            base[i][++base[i][0]] = base[i - 1][j];
        }
    }
}

int main() {
    int t = _read();
    while (t--) {
        int n = _read();
        int m = _read();
        for (int i = 1; i <= n; i++) 
        {
            a[i] = _read();
            add(i);
        }
        int lastans = 0;
        for (int i = 0; i < m; i++) {
            int op = _read();
            if (op == 1) {
                a[++n] = _read();
                a[n] ^= lastans;
                add(n);
            }
            else {
                int l = _read();
                int r = _read();
                l = (l ^ lastans) % n + 1;
                r = (r ^ lastans) % n + 1;
                if (l > r)swap(l, r);
                lb.ini();
                for (int j = 1; j <= base[r][0] && base[r][j] >= l; j++) {
                    lb.insert(a[base[r][j]]);//032
                }
                lastans = lb.Max(0);
                printf("%d\n", lastans);
            }
        }
    }
}

5.1013.这个题目看起来很长,其实概括一下就是这样的,给二维平面上n个点的坐标(x1,x2),每个点有一个属性-1,1.现在找一条直线,可以把所有点划分为两部分,一侧都是1另一侧是-1,问存不存在这样的直线。转化一下,把相同属性的点求一个凸包,然后看一下这两个凸包有没有交集即可。(网上随便找了一个板子AC的,就不放了)

6.1011.数论题目。就是推导有点多,但是也都是基操,然后就是__int128。也没有特别困难的地方(???什么sum(gcd(a,i))不好求??)

7.1012.个人觉得这个题目还是挺神的,也可能是一个套路,因为求k次前缀和我还真不会。至少我没发现系数就是组合数,那一点我很难发现。发现了之后,那么简单了,就是求两个数组的卷子,直接NTT了。

8.剩下的题目是队友在搞,我还没补。

1006:SAM的应用,队友上来一发WA了,然后怀疑人生,重新去学了clj的PPT。

1001:DP。这个我上来就看出来是一个DP,知道不好惹,就溜了。

1009:贪心,我开始写了一个假算法,队友过了。

 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值