1.首先,总的来说HDU多校这次难度的是有的,但是并非表现得那么难,可能有很多队伍没有发挥好。
2.1005 Path.这个题目出了好多次了,基本套路,扣最短路跑最大流。
后记:有大佬说代码有些冗余,不用传dis数组,其实无所谓的,因为DJ哪个函数里已经每次把dis每次给重新赋值了。
using namespace std;
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行代码即可解决。开始想了一个二分时间的写法,但是没有仔细读题,我以为一辆车通过之后对其他车不再有影响,实际上这个影响一直存在。
using namespace std;
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).
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:贪心,我开始写了一个假算法,队友过了。