Helvetic Coding Contest 2019 online mirror (teams allowed, unrated)
http://codeforces.com/contest/1184
A1
找一对整数,使x^x+2xy+x+1=r
变换成一个分式,保证整除
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include <map> #define fo(i,l,r) for(int i = l;i <= r;i++) #define ll long long using namespace std; const int maxn = 300050; const ll mod = 1e9+7; inline ll read(){ ll x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9'){ if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9'){ x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } ll r; int main(){ r=read(); for(ll i = 1;i <= 1000000;i++){ if(i*i>r)break; ll t = r-i*i-i-1; if(t<=0)continue; if(t%(i+i)==0){ cout<<i<<" "<<t/(i+i); return 0; } } cout<<"NO"; return 0; }
A2
给定一个二进制串,对于整数k来说,若存在另一个二进制串x,时x与x右移k位异或的结果等于这个二进制串,k就是合法的,求合法的k的数量。
先考虑x,第一个位确定了,后面1+k,1+2k...的位也确定了,最后会回到第一位,这时需要与之前假定的第一位一致。于是这就形成了一个循环。循环长度为lcm(k,n)/k=n/gcd(k,n)。
对k,按gcd(k,n),进行归纳求解,将所有k映射到gcd(k,n),然后求出gcd(k,n)的答案,只需要遍历所有循环的位置,再查看是否合法。
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include <map> #define fo(i,l,r) for(int i = l;i <= r;i++) #define ll long long using namespace std; const int maxn = 200050; const ll mod = 1e9+7; inline ll read(){ ll x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9'){ if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9'){ x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } int n; int a[maxn]; char s[maxn]; bool vis[maxn]; bool can[maxn]; int gcd(int a,int b){ return b==0?a:gcd(b,a%b); } int main(){ n=read(); scanf("%s",s+1); fo(i,1,n){ a[i] = s[i] - '0'; } int ans = 0,tot = 0; fo(i,1,n){ int g = gcd(i,n); if(vis[g]){ ans += can[g]; }else{ vis[g]=true; bool ok=false; fo(j,1,g){ ok=true; tot=0; for(int k = j;k <= n;k += g){ tot += a[k]; } if(tot%2){ ok=false; break; } } if(ok) can[g]=true; ans += can[g]; } } cout<<ans; return 0; }
B1
舰队攻打敌军,只能攻打防御力不大于它攻击力的,敌军掉落的金币不等,问每个船能打多少钱
排序即可
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include <map> #define fo(i,l,r) for(int i = l;i <= r;i++) #define ll long long using namespace std; const int maxn = 200050; const ll mod = 1e9+7; inline ll read(){ ll x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9'){ if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9'){ x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } int s,b; struct dat{ int pos; int val; friend bool operator < (dat a,dat b){ return a.val < b.val; } }shp[maxn],ene[maxn]; ll gold[maxn]; int main(){ s=read(); b=read(); fo(i,1,s){ shp[i].pos=i; shp[i].val=read(); } fo(i,1,b){ ene[i].val=read(); ene[i].pos=read(); } sort(shp+1,shp+1+s); sort(ene+1,ene+1+b); int pos=0; ll ans = 0; fo(i,1,s){ while(pos<b&&ene[pos+1].val<=shp[i].val){ pos++; ans += ene[pos].pos; } gold[shp[i].pos]=ans; } fo(i,1,s){ printf("%I64d ",gold[i]); } return 0; }
B2
敌军在一个图上攻打舰队,每个敌军只能打防御力不大于他的船,并且两者距离不大于他的燃油量。每个敌军负责打一个人,掠夺k金币。
我军可以花h金币建造假船,一定会被一个敌军攻击,且不损失金币。求损失最少的策略。
因为金币数量是固定的,容易证明要么全用假船吸引,要么一个假船都不放。
先跑多源最短路,然后匹配即可。
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include <map> #define fo(i,l,r) for(int i = l;i <= r;i++) #define ll long long using namespace std; const int maxn = 200050; const ll mod = 1e9+7; inline ll read(){ ll x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9'){ if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9'){ x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } int n,m; int dis[105][105]; int s,b; ll kk,hh; struct ship{ int x; ll f; ll a; }ships[1050]; struct ene{ int x; ll d; }enes[1050]; int uN,vN; int g[1050][1050]; int linker[1050]; int used[1050]; bool dfs(int u){ for(int v = 0;v < vN;v++){ if(g[u][v] && !used[v]){ used[v] = true; if(linker[v]==-1||dfs(linker[v])){ linker[v]=u; return true; } } } return false; } int hungary(){ int res = 0; memset(linker,-1, sizeof(linker)); for(int u = 0;u < uN;u++){ memset(used,false, sizeof(used)); if(dfs(u))res++; } return res; } int main(){ n=read(); m=read(); int u,v; fo(i,1,n){ fo(j,1,n){ dis[i][j]=1e7; } dis[i][i]=0; } fo(i,1,m){ u=read(); v=read(); dis[u][v] = 1; } fo(k,1,n){ fo(i,1,n){ fo(j,1,n){ if(k==i||k==j||i==j)continue; dis[i][j] = min(dis[i][j],dis[i][k]+dis[k][j]); } } } s=read();b=read();kk=read();hh=read(); fo(i,1,s){ ships[i].x=read(); ships[i].a=read(); ships[i].f=read(); } fo(i,1,b){ enes[i].x=read(); enes[i].d=read(); } uN=s; vN=b; fo(i,1,s){ fo(j,1,b){ if(ships[i].a >= enes[j].d && dis[ships[i].x][ships[j].x] <= ships[i].f) g[i-1][j-1]=1; } } ll ans = min((ll)s*hh,kk*hungary()); cout<<ans; return 0; }
C1
给若干个在长方形边上的点和一个不在边上的点,找出那个长方形
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include <map> #define fo(i,l,r) for(int i = l;i <= r;i++) #define ll long long using namespace std; const int maxn = 55; const ll mod = 1e9+7; inline ll read(){ ll x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9'){ if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9'){ x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } int n; int x[maxn],y[maxn]; bool check(int x1,int x2,int y1,int y2){ int cnt = 0,t=0; bool debug=false; if(x1==19&&x2==45&&y1==1&&y2==27)debug=true; fo(i,1,n){ if(x[i]<x1||x[i]>x2||y[i]<y1||y[i]>y2){ cnt++; t=i; }else if(x[i]!=x1&&x[i]!=x2&&y[i]!=y1&&y[i]!=y2){ cnt++; t=i; } if(cnt>1)return false; } if(cnt == 0) return false; cout<<x[t]<<" "<<y[t]; return true; } int main(){ n=read(); n = n*4+1; fo(i,1,n){ x[i]=read(); y[i]=read(); } fo(i,0,50){ fo(j,i,50){ fo(k,0,50){ fo(l,k,50){ if(check(i,j,k,l)) return 0; } } } } return 0; }
D1
一个长度n的线段和一个特殊点,每次插入或者切掉头或尾一段,每次询问特殊点的位置。
维护即可。
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include <map> #define fo(i,l,r) for(int i = l;i <= r;i++) #define ll long long using namespace std; const int maxn = 305; const ll mod = 1e9+7; inline ll read(){ ll x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9'){ if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9'){ x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } int n,k,m,t; int main(){ n=read();k=read(); m=read();t=read(); int opt,v; fo(i,1,t){ opt=read(); v=read(); if(opt==1){ n++; if(v<=k)k++; }else{ if(k > v){ n-=v; k-=v; }else{ n=v; } } cout<<n<<" "<<k<<endl; } return 0; }
E1
给一个图,求出最小生成树,问如果第一条边可能在最小生成树里,其边权最多是多大
看看什么时候这条边对应的两个点处于同一个集合中。
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include <map> #define fo(i,l,r) for(int i = l;i <= r;i++) #define ll long long using namespace std; const int maxn = 100050; const ll mod = 1e9+7; inline ll read(){ ll x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9'){ if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9'){ x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } int n,m; int f[maxn]; int findf(int x){ return f[x]==x?x:f[x]=findf(f[x]); } struct edge{ int u; int v; ll w; friend bool operator < (edge a,edge b){ return a.w < b.w; } }e[maxn*10]; int main(){ n=read();m=read(); fo(i,1,m){ e[i].u=read(); e[i].v=read(); e[i].w=read(); } fo(i,1,n){ f[i]=i; } int aa = e[1].u,bb=e[1].v; sort(e+1,e+1+m); fo(i,1,m){ int u=e[i].u; int v=e[i].v; int fu=findf(u),fv=findf(v); if(fu==fv) continue; if(e[i].u==aa&&e[i].v==bb)continue; f[fu]=fv; if(findf(aa)==findf(bb)){ cout<<e[i].w; return 0; } } cout<<1000000000; return 0; }
E2
与上一个问题类似,但是这次要求除了最小生成树的边之外所有边的答案
先求最小生成树,两个点之间加一条边,则形成一个环,由这条边和两者的树上最短路径的边组成,用lca求最短路径上的边的最大边权即可。
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include <map> #define fo(i,l,r) for(int i = l;i <= r;i++) #define ll long long using namespace std; const int maxn = 100050; const ll mod = 1e9+7; inline ll read(){ ll x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9'){ if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9'){ x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } int n,m; int f[maxn]; int findf(int x){ return f[x]==x?x:f[x]=findf(f[x]); } struct edge{ int u; int v; ll w; int id; friend bool operator < (edge a,edge b){ return a.w < b.w; } }e[maxn*10]; bool chs[maxn*10]; bool cmp(edge a,edge b){ return a.id < b.id; } int dep[maxn]; int fa[maxn][25]; ll fw[maxn][25]; struct te{ int v; ll w; }now,nxt; vector<te> g[maxn]; void dfs(int x,int p){ int k = 0; while(fa[x][k]&&fa[fa[x][k]][k]){ fa[x][k+1] = fa[fa[x][k]][k]; fw[x][k+1] = max(fw[x][k],fw[fa[x][k]][k]); k++; } int sz = g[x].size(); int v=0; fo(i,0,sz-1){ v=g[x][i].v; if(v==p)continue; fa[v][0] = x; fw[v][0] = g[x][i].w; dep[v] = dep[x] + 1; dfs(v,x); } } ll getans(int u,int v){ if(dep[u]<dep[v]) swap(u,v); ll mx = 0; int k = 0,dif=dep[u]-dep[v]; int ru=u,rv=v; while(dif){ if(dif&1){ mx = max(fw[u][k],mx); u = fa[u][k]; } dif >>= 1; k++; } if(u==v)return mx; k = 0; while(k >= 0){ if(fa[u][k] != fa[v][k]){ mx = max(mx,fw[u][k]); mx = max(mx,fw[v][k]); u = fa[u][k]; v = fa[v][k]; k++; }else{ k--; } } mx = max(mx,fw[u][0]); mx = max(mx,fw[v][0]); return mx; } int main(){ n=read();m=read(); fo(i,1,m){ e[i].u=read(); e[i].v=read(); e[i].w=read(); e[i].id=i; } fo(i,1,n){ f[i]=i; } sort(e+1,e+1+m); fo(i,1,m){ int u=e[i].u; int v=e[i].v; int fu=findf(u),fv=findf(v); if(fu==fv) continue; f[fu]=fv; chs[e[i].id]=true; now.w=e[i].w; now.v=v; g[u].push_back(now); now.v=u; g[v].push_back(now); } dfs(1,0); sort(e+1,e+1+m,cmp); fo(i,1,m){ if(chs[i])continue; printf("%I64d\n",getans(e[i].u,e[i].v)); } return 0; }