《HDU 2020 多校第二场》
Total Eclipse
思路:先考虑一种普遍解法。
对于每个极大连通块,一次次块上的全部点减去1,断开为子图后继续重复减1,显然是最优的减法。
但是减去后遍历边的复杂度过高,且b值也很大,所以考虑成加边的形式。
首先将点按权值降序。那么我们可以采取将最大的点减成第二大的点,然后两个点又一起减成第三大的点,然后三个点一起减成第四大.....
那么显然这中间能不能一起减去这个代价会是分界,当两个点可以从之前的点间接相连,或者他们可以直接相连时,那么显然可以一起减,
因为之前的点显然和他们已经减到了相同代价。如果不能直接或者间接相连,那么就是两个不同的连通块里的点,那么就要*连通块的个数。
那么每一次的代价就是 num(连通块) * (b[i] - b[i+1]).
所以,我们用并查集来维护连通块的个数,每次计算一个点时,先将这个点合并到和它相连的点且之前出现过的点的连通块中。
Code:
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef long double ld; typedef pair<int,int> pii; const int N = 1e5+5; const LL Mod = 1e9+9; #define pi acos(-1) #define INF 1e8 #define INM INT_MIN #define dbg(ax) cout << "now this num is " << ax << endl; inline int read() { int x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } struct Node{ int val,id; bool operator < (const Node &a)const{ return val > a.val; } }p[N]; int vis[N],fa[N]; vector<int> G[N]; int Find(int x) { return x == fa[x] ? x : fa[x] = Find(fa[x]); } int main() { freopen("data1.in","r",stdin); freopen("data1.out","w",stdout); int t;t = read(); while(t--) { int n,m; n = read(),m = read(); for(int i = 1;i <= n;++i) { p[i].val = read(); p[i].id = fa[i] = i; G[i].clear(); vis[i] = 0; } sort(p+1,p+n+1); p[n+1].val = 0; while(m--) { int u,v; u = read(),v = read(); G[u].push_back(v); G[v].push_back(u); } LL ans = 0; int cnt = 0;//连通块数量 for(int i = 1;i <= n;++i) { int x = p[i].id; vis[x] = 1; cnt++; for(auto v : G[x]) { if(!vis[v]) continue; int xx = Find(x),yy = Find(v); if(xx != yy) { fa[xx] = yy; cnt--; } } ans += 1LL*(p[i].val-p[i+1].val)*cnt; } printf("%lld\n",ans); } system("pause"); return 0; }
Lead of Wisdom
思路:暴力搜素
如果直接搜索会超时。
考虑这样一种搜索树。
对于k = 3,4的情况,显然可以直接停止,但是还搜索会提高复杂度。
所以对于k种类的个数为0的种类,就不再存取。
那么对于每一层,对于剩下的k种,平均下每层大概$\frac{n}{k}$个数,那么复杂度就是$k^{\frac{n}{k}}$
最坏情况下n = 50,k为3的时候复杂度取到最高,还是能过。
Code:
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef long double ld; typedef pair<int,int> pii; const int N = 1e5+5; const LL Mod = 1e9+9; #define pi acos(-1) #define INF 1e8 #define INM INT_MIN #define dbg(ax) cout << "now this num is " << ax << endl; inline int read() { int x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } int n,k,cnt[55],tim; LL ans = 0; struct Node{int a,b,c,d;}; vector<Node> G[55]; LL cal(LL a,LL b,LL c,LL d) { return (100+a)*(100+b)*(100+c)*(100+d); } void dfs(int x,int a,int b,int c,int d) { if(x == tim+1) { ans = max(ans,cal(a,b,c,d)); return ; } for(auto v : G[x]) dfs(x+1,a+v.a,b+v.b,c+v.c,d+v.d); } int main() { int t;t = read(); while(t--) { n = read(),k = read(); ans = tim = 0; for(int i = 1;i <= k;++i) G[i].clear(); memset(cnt,0,sizeof(cnt)); for(int i = 1;i <= n;++i) { int kk;kk = read(); int a,b,c,d; if(cnt[kk] == 0) cnt[kk] = ++tim; a = read(),b = read(),c = read(),d = read(); G[cnt[kk]].push_back(Node{a,b,c,d}); } dfs(1,0,0,0,0); printf("%lld\n",ans); } system("pause"); return 0; }
The Oculus
思路:需要注意一下这里的数据范围,代表的是长度。
那么显然直接处理出f[i]会爆longlong。
但是因为这里值涉及到了加法和乘法。那么取模显然不会影响结果。
所以先对f[i]进行hash,运用双模数,构建f1[i],f2[i]的键值对,这样基本就能保证数据与下标的对不重叠出现。
那么,显然我们要得到的就是A*B-C。对应的下标。
直接根据A*B-C对双模数取模后的键值来找到对应的下标即可。
但是这里map会T。所以要用到unorderedm_map.
但是unordered_map不支持pii的键值,所以只能放入<LL,int>形式。
但是可以通过构造一个比较单独型的LL值来继续保持下标对应的唯一性。
所以这里用了f1*mod+f2来构建hash的键值
Code:
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef long double ld; typedef pair<LL,LL> pii; const int N = 1e6+5; const LL Mod = 1e9+9; const LL mod = 1e9+7; #define pi acos(-1) #define INF 1e8 #define INM INT_MIN #define dbg(ax) cout << "now this num is " << ax << endl; inline int read() { int x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } LL f1[N<<1],f2[N<<1]; unordered_map<LL,int> mp; inline void init() { f1[1] = f2[1] = 1; f1[2] = f2[2] = 2; mp[f1[1]*mod+f2[1]] = 1; mp[f1[2]*mod+f2[2]] = 2; for(int i = 3;i < (N<<1);++i) { f1[i] = (f1[i-1]+f1[i-2])%Mod; f2[i] = (f2[i-1]+f2[i-2])%mod; mp[f1[i]*mod+f2[i]] = i; } } int main() { init(); /* freopen("data1.in","r",stdin); freopen("data1.out","w",stdout);*/ int t;t = read(); while(t--) { LL A1 = 0,A2 = 0; LL B1 = 0,B2 = 0; LL C1 = 0,C2 = 0; int n1;n1 = read(); for(int i = 1;i <= n1;++i) { int x;x = read(); if(x) { A1 = (A1+f1[i])%Mod; A2 = (A2+f2[i])%mod; } } int n2;n2 = read(); for(int i = 1;i <= n2;++i) { int x;x = read(); if(x) { B1 = (B1+f1[i])%Mod; B2 = (B2+f2[i])%mod; } } int n3;n3 = read(); for(int i = 1;i <= n3;++i) { int x;x = read(); if(x) { C1 = (C1+f1[i])%Mod; C2 = (C2+f2[i])%mod; } } LL tmp1 = ((A1*B1)%Mod-C1+Mod)%Mod; LL tmp2 = ((A2*B2)%mod-C2+mod)%mod; int ans = mp[tmp1*mod+tmp2]; printf("%d\n",ans); } return 0; }
New Equipments
思路:首先可以发现路径的代价是个二次函数,然后由于每个人只能匹配一个机器。
且一个机器只能对应一个人,那就是一个二分图的模型。
如果每个人去每个机器都建边,m是1e8的数据,显然会TLE。
但是可以发现的是n是只有50,而且一个人最多只会对应1个机器,那么我们让每个人和代价最小的n个机器连起来。
就能保证每个都能有匹配的对象。
同时由于这是个开口向上的二次函数,所以可以先利用二次函数的特性找到最小的点,然后向左右扩展连满n条边。
这里用了三分找最小点。
那么显然建立一个超级源点s和每个人都建立1的容量,0的费用。超级汇点和每个机器建立1的容量,0的费用。
对于每个人的费用就是之前的总费用+当前的费用流。
因为每条边都是1的容量,所以我们其实会spfa增广n次。
每次增广都将第i个点的费用流算出。
为什么当前的费用要加上之前的总费用。
我是这样理解的:之前的费用流算出后,残余网络上就会经过那些负边权的反向边,而反向边的总代价就是之前的费用流的总和。
中间算边的大小的时候,数据开小了,只算了人和机器的边数,调了蛮久..
Code:
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef long double ld; typedef pair<int,int> pii; const int N = 1e5+5; const int M = 1e6+6; const LL Mod = 2505; #define pi acos(-1) #define INF 1e18 #define INM INT_MIN #define dbg(ax) cout << "now this num is " << ax << endl; inline LL read() { LL x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } /* work [1,n] equ [1+n,mp.size+n] s = 0,t = mp.size+n+1 */ int n,m,s,t,cnt = -1,tim = 0,num = 0; LL head[N],pre[N],cal[N],dis[N],vis[N],a[N],b[N],c[N],ans[N],ma; struct Node{int to,next;LL dis,flow;}e[N<<1]; map<int,int> mp; inline void add(int u,int v,LL f,LL w)//flow - cost { e[++cnt].to = v,e[cnt].dis = w,e[cnt].flow = f,e[cnt].next = head[u],head[u] = cnt; } void init() { cnt = -1,tim = 0,num = 0,ma = 0; mp.clear(); memset(head,-1,sizeof(head)); memset(pre,0,sizeof(pre)); memset(cal,0,sizeof(cal)); } LL slove(LL a,LL b,LL c,int j) { return a*j*j+b*j+c; } bool spfa() { memset(vis,0,sizeof(vis)); for(int i = 0;i <= t;++i) dis[i] = INF; queue<int> Q; Q.push(s); dis[s] = 0,vis[s] = 1,cal[s] = INF; while(!Q.empty()) { int u = Q.front(); Q.pop(); vis[u] = 0; for(int i=head[u];i!=-1;i=e[i].next) { int v = e[i].to; LL d = e[i].dis,flow = e[i].flow; if(flow <= 0) continue; if(dis[v] > dis[u]+d) { dis[v] = dis[u]+d; cal[v] = min(cal[u],flow); pre[v] = i; if(!vis[v]) vis[v] = 1,Q.push(v); } } } if(dis[t] == INF) return false; return true; } void MCMF() { while(spfa()) { int x = t; ma += dis[t]*cal[t]; ans[++num] = ma; while(x != s) { int i = pre[x]; e[i].flow -= cal[t]; e[i^1].flow += cal[t]; x = e[i^1].to; } } } void check(int i) { int L = 1,r = m,tmp; while(L < r) { int mid = (L+r)>>1; int smid = (mid+r)>>1; if(slove(a[i],b[i],c[i],mid) > slove(a[i],b[i],c[i],smid)) L = mid,tmp = mid; else r = smid,tmp = smid; } if(mp[tmp] == 0) mp[tmp] = ++tim; add(i,n+mp[tmp],1,slove(a[i],b[i],c[i],L)); add(n+mp[tmp],i,0,-slove(a[i],b[i],c[i],L)); L = tmp-1,r = tmp+1; for(int j = 1;j < n;++j) { if(L >= 1 && (slove(a[i],b[i],c[i],L) <= slove(a[i],b[i],c[i],r))) { if(mp[L] == 0) mp[L] = ++tim; add(i,n+mp[L],1,slove(a[i],b[i],c[i],L)); add(n+mp[L],i,0,-slove(a[i],b[i],c[i],L)); L--; } else if(r <= m) { if(mp[r] == 0) mp[r] = ++tim; add(i,n+mp[r],1,slove(a[i],b[i],c[i],r)); add(n+mp[r],i,0,-slove(a[i],b[i],c[i],r)); r++; } } } int main() { freopen("data1.in","r",stdin); freopen("data1.out","w",stdout); int tt;tt = read(); while(tt--) { init(); n = read(),m = read(); for(int i = 1;i <= n;++i) a[i] = read(),b[i] = read(),c[i] = read(); for(int i = 1;i <= n;++i) check(i); s = 0,t = n+tim+1; for(int i = 1;i <= n;++i) add(s,i,1,0),add(i,s,0,0); for(int i = 1;i <= tim;++i) add(i+n,t,1,0),add(t,i+n,0,0); MCMF(); for(int i = 1;i <= n;++i) printf("%lld%c",ans[i],i == n ? '\n' : ' '); } system("pause"); return 0; }