2018 China Collegiate Programming Contest Final
A Mischievous Problem Setter
题目链接
题意:n道题,每道题具有难度和时间,按照难度解题,求m分钟解题数量
思路:按顺序计算总和即可
#include<bits/stdc++.h> using namespace std; using ll = long long; const int maxn = 1e5 + 5; const int inf = 0x3f3f3f3f; struct node { int D, T; }k[maxn]; int main() { int t; scanf("%d", &t); int ca = 0; while (t--) { printf("Case %d: ", ++ca); int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d", &k[i].D); } for (int i = 1; i <= n; i++) { scanf("%d", &k[i].T); } sort(k + 1, k + 1 + n, [&](node a, node b) { return a.D < b.D; }); int cnt = 0; for (int i = 1; i <= n; i++) { //cout << k[i].D << endl; if (m >= k[i].T) { m -= k[i].T; cnt++; } else { break; } } cout << cnt << endl; } } /* 2 5 120 5 10 20 35 100 10 20 35 100 100000 13 300 52 55 82 11 62 79 38 8 58 28 1 70 32 27 62 45 77 22 69 34 43 21 43 85 22 36 */
B Balance of the Force
题目链接
题意:每个人都可以分属为光明或黑暗阵营,对其阵营产生ai和bi的贡献值,要求每个人分属完后的max贡献-min贡献最小。人与人之间有厌恶关系,即两个人厌恶就不能在同一阵营。
思路:通过种类并查集或者DFS染色来区分不同阵营的人。可以形成若干个集合,记录集合最大和最小值,对于一个集合,其存在光明和黑暗两种可能,排序后遍历一维,用线段树记录另一维的最值,当两个集合属于同一个并查集就覆盖取最优情况。
#include <bits/stdc++.h> #define maxn 200005 #define inf 0x3f3f3f3f using namespace std; int n,m; int cnt,tot; int flag=0; int fa[maxn]; int value[maxn]; int vis[maxn]; int in_maxx[maxn]; int in_tree[maxn]; int find(int x){ if(x!=fa[x]){ int t=fa[x]; fa[x]=find(fa[x]); value[x]=(value[x]+value[t])%2; } return fa[x]; } void mix(int x,int y,int s){ int fx=find(x); int fy=find(y); if(fx!=fy){ fa[fx]=fy; value[fx]=value[x]^value[y]^s; }else if(value[x]==value[y]){ flag=1; } } void init(){ flag=0; cnt=0; tot=0; for(int i=1;i<=n;i++){ fa[i]=i; value[i]=0; vis[i]=0; } } struct point{ int x,y; }a[maxn]; struct node{ int maxx,minn; int id; node(int a=inf,int b=-inf,int c=0):minn(a),maxx(b),id(c){} }t[maxn<<1]; bool cmp(node a,node b){ if(a.minn==b.minn) return a.maxx<b.maxx; return a.minn>b.minn; } int tree[maxn<<2]; void Build(int l,int r,int rt){ tree[rt]=-inf; if(l==r)return; int mid=(l+r)>>1; Build(l,mid,rt<<1); Build(mid+1,r,rt<<1|1); } void pushup(int rt){ tree[rt]=max(tree[rt<<1],tree[rt<<1|1]); } void update(int index,int x,int l,int r,int rt){ if(l==r){ tree[rt]=x; return; } int mid=(l+r)>>1; if(index<=mid) update(index,x,l,mid,rt<<1); else update(index,x,mid+1,r,rt<<1|1); pushup(rt); } int main(){ int T; scanf("%d",&T); for(int cas=1;cas<=T;cas++){ scanf("%d%d",&n,&m); init(); int u,v; for(int i=1;i<=m;i++){ scanf("%d%d",&u,&v); mix(u,v,1); } for(int i=1;i<=n;i++){ scanf("%d%d",&a[i].x,&a[i].y); if(flag)continue; u=find(i); if(!vis[u]){ cnt+=2; vis[u]=cnt-1; if(!value[i]){ t[cnt-1]=node(a[i].x,a[i].x,++tot); t[cnt]=node(a[i].y,a[i].y,tot); }else{ t[cnt-1]=node(a[i].y,a[i].y,++tot); t[cnt]=node(a[i].x,a[i].x,tot); } in_tree[tot]=0; }else{ if(!value[i]){ t[vis[u]].minn=min(t[vis[u]].minn,a[i].x); t[vis[u]].maxx=max(t[vis[u]].maxx,a[i].x); t[vis[u]+1].minn=min(t[vis[u]+1].minn,a[i].y); t[vis[u]+1].maxx=max(t[vis[u]+1].maxx,a[i].y); }else{ t[vis[u]].minn=min(t[vis[u]].minn,a[i].y); t[vis[u]].maxx=max(t[vis[u]].maxx,a[i].y); t[vis[u]+1].minn=min(t[vis[u]+1].minn,a[i].x); t[vis[u]+1].maxx=max(t[vis[u]+1].maxx,a[i].x); } } } printf("Case %d: ",cas); if(flag){ printf("IMPOSSIBLE\n"); continue; } Build(1,tot,1); sort(t+1,t+cnt+1,cmp); int sum=0,id=0,ans=inf; for(int i=1;i<=cnt;i++){ // cout<<t[i].minn<<' '<<t[i].maxx<<endl; if(sum==tot){ if(t[i].maxx<t[in_maxx[t[i].id]].maxx) update(in_tree[t[i].id],t[i].maxx,1,tot,1); ans=min(ans,tree[1]-t[i].minn); }else{ if(!in_tree[t[i].id]){ sum++; in_tree[t[i].id]=++id; in_maxx[t[i].id]=i; update(id,t[i].maxx,1,tot,1); if(sum == tot) ans = min(ans, tree[1] - t[i].minn); }else if(t[i].maxx<t[in_maxx[t[i].id]].maxx) update(in_tree[t[i].id],t[i].maxx,1,tot,1); } } printf("%d\n",ans); } }
G Pastoral Life in Stardew Valley
题目链接
题意:在 n×m 的棋盘上选择两个长方形使一个长方形完全包含另一个长方形,求方案数
思路:
#include<bits/stdc++.h> using namespace std; using ll = long long; const ll maxn = 1e5 + 5; const ll inf = 0x3f3f3f3f; const ll mod = 1e9 + 7; ll x3[maxn], x2[maxn], x1[maxn]; void init() { x3[0] = 0; x2[0] = 0; x1[0] = 0; for (ll i = 1; i < maxn; i++) { x3[i] = (x3[i - 1] + i * i*i%mod) % mod; x2[i] = (x2[i - 1] + i * i%mod) % mod; x1[i] = (x1[i - 1] + i % mod) % mod; // cout << i << " " << x3[i] << " " << x2[i] << " " << x1[i] << endl; } } ll qpow(ll A, ll B) { ll t = 1; while (B) { if ((B & 1)) { t = t * A%mod; } A = A * A%mod; B = B >> 1; } return t; } ll fun(ll n) { ll res = (((mod - x3[n - 2] % mod) % mod + (n - 2)*x2[n - 2] % mod + ((n - 1)*x1[n - 2] % mod) % mod)) % mod; // printf("*%d ", res); res = res * qpow(2ll, mod - 2) % mod; return res; } int main() { init(); ll t; scanf("%lld", &t); int ca = 0; while (t--) { printf("Case %d: ", ++ca); ll n, m; scanf("%lld%lld", &n, &m); if (n <= 2 || m <= 2)puts("0"); else printf("%lld\n", fun(n)*fun(m)%mod); } }
I Cockroaches
题目链接
题意:二维平面上有若干只蟑螂,一种工具可以对一个点释放,使同一行和同一列蟑螂都死掉,求最多杀死蟑螂数和方案数。
思路:先求出最多的蟑螂数量,然后通过已知最多的蟑螂数量推出相对应多少中方案数。
#include<bits/stdc++.h> using namespace std; using ll = long long; const ll maxn = 1e5 + 5; const ll inf = 0x3f3f3f3f; const ll mod = 1e9 + 7; struct Hash { ll a[maxn], cnt; void init() { cnt = 0; } void add(ll x) { a[++cnt] = x; } void work() { sort(a + 1, a + 1 + cnt); cnt = unique(a + 1, a + 1 + cnt) - a - 1; } ll get(ll x) { return lower_bound(a + 1, a + 1 + cnt, x) - a; } }hsx,hsy; ll x[maxn], y[maxn]; ll numx[maxn], numy[maxn]; vector<ll>v[maxn]; ll fun_ans() { ll ma = 0; ll cnt = 0; for (ll i = 1; i <= hsx.cnt; i++) { if (ma > numx[i]) { continue; } else if (ma == numx[i]) { cnt++; } else{ ma = numx[i]; cnt = 1; } } //cout << ma << "**" << cnt << endl; ll res = 0; for (ll i = 1; i <= hsy.cnt; i++) { ll resy = numy[i]; ll ct = 0; for (ll j : v[i]) { if (numx[j] == ma) { ct++; } } if (ct == cnt) { res = max(res, ma + numy[i] - 1); } else { res = max(res, ma + numy[i]); } } ll second_cnt = 0; for (ll i = 1; i <= hsx.cnt; i++) { if (numx[i] == ma - 1) { second_cnt++; } } //cout << ma << " ma" << endl; ll ans = 0; for (ll i = 1; i <= hsy.cnt; i++) { ll resy = numy[i]; // ll ct1 = 0, ct2 = 0; for (ll j : v[i]) { if (numx[j] == ma) { ct1++; } if (numx[j] == ma - 1) { ct2++; } } if (ct1 == cnt) { if(resy + ma - 1 == res) ans += cnt + second_cnt - ct2; } else { if (resy + ma == res) ans += cnt - ct1; } //cout << ans << " ans:" << endl; } printf("%lld ", res); if (res == 2)return ans / 2; else return ans; } int main() { ll t; scanf("%lld", &t); ll ca = 0; while (t--) { ll n; scanf("%lld", &n); hsx.init(); hsy.init(); for (ll i = 1; i <= n; i++) { numx[i] = 0; numy[i] = 0; v[i].clear(); scanf("%lld%lld", &x[i], &y[i]); hsx.add(x[i]); hsy.add(y[i]); } hsx.work(); hsy.work(); for (ll i = 1; i <= n; i++) { x[i] = hsx.get(x[i]); y[i] = hsy.get(y[i]); numx[x[i]]++; numy[y[i]]++; v[y[i]].push_back(x[i]); } /*cout <<"hsx,hsy:"<< hsx.cnt << " " << hsy.cnt << endl; for (ll i = 1; i <= n; i++) { cout <<"(i,j) "<< x[i] << " " << y[i] << endl; }*/ printf("Case %lld: ", ++ca); ll ans = fun_ans(); printf("%lld\n", ans); } } /* 20 5 1 2 1 3 2 3 4 5 6 7 3 1 2 2 3 3 1 */
J Mr. Panda and Sequence Puzzle
题目链接
RSA解密,待补
L Ultra Weak Goldbach’s Conjecture
题目链接
题意:一个数分为六个素数和,求任意可行解。
思路:题目告诉一个偶数可以分为两个素数,一个奇数可以分为五个素数。
可以想到对于一个偶数可以分为 2+2+2+2+素数+素数。一个奇数可以分为3+2+2+2+素数+素数。枚举一个素数,判另一个素数即可。
#include <bits/stdc++.h> #define maxn 1000005 typedef long long ll; using namespace std; ll x; int prime[maxn]; ll p[maxn]; int tot; void init(){ for(int i=2;i<=1000000;i++){ if(prime[i]==0) p[++tot]=i; for(int j=1;j<=tot&&i*p[j]<=1000000;j++){ prime[i*p[j]]=1; if(i%p[j]==0) break; } } } bool check(ll x){ for(ll i=2;i*i<=x;i++){ if(x%i==0) return false; } return x!=1; } int main(){ int t; init(); scanf("%d",&t); for(int cas=1;cas<=t;cas++){ scanf("%lld",&x); printf("Case %d: ",cas); if(x<=11){ printf("IMPOSSIBLE\n"); continue; } if(x%2==0){ x-=8; for(int i=1;i<=tot;i++){ // cout<<i<<" "<<p[i]<<" "<<x-p[i]<<endl; if(check(1ll*(x-p[i]))) { printf("%lld %lld 2 2 2 2\n",p[i],1ll*(x-p[i])); break; } } }else{ x-=9; for(int i=1;i<=tot;i++){ if(check(x-p[i])) { printf("%lld %lld 2 2 2 3\n",p[i],1ll*(x-p[i])); break; } } } } }