2017-2018 ACM-ICPC, NEERC, Moscow Subregional Contest

时间:2022-05-02 14:39:01

A. Advertising Strategy

最优策略一定是第一天用$y$元,最后一天再用$x-y$元补满。

枚举所有可能的$y$,然后模拟即可,天数为$O(\log n)$级别。

时间复杂度$O(x\log n)$。

#include<cstdio>
typedef long long ll;
const ll inf=1LL<<60;
ll min(ll a,ll b){return a<b?a:b;}
ll cal(ll n,ll x,ll o){
ll ans=1;
ll a=0,b,k,pre;
while(1){
pre=a;
k=min(n-a,x);
b=a+k;
x-=k;
a=b+min(b,(n-b)/2);
if(a==n)return ans;
if(a+o>=n)return ans+1;
if(a==pre)return inf;
ans++;
}
}
int main(){
ll n,x;
scanf("%lld%lld",&n,&x);
ll ans=inf;
for(int i=0;i<x;i++)ans=min(ans,cal(n,x-i,i));
printf("%lld",ans);
}

  

B. Byteland Trip

留坑。

C. Carpet

对树进行轻重链剖分,那么把重儿子往右挂,轻儿子往下挂即可。层数$O(\log n)$。

#include<cstdio>
const int N=100010,K=25;
int n,i,x,y,g[N],v[N<<1],nxt[N<<1],ed,size[N],son[N];
int X[N],Y[N];
int cnt[K];
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs(int x,int y){
size[x]=1;
for(int i=g[x];i;i=nxt[i])if(v[i]!=y){
dfs(v[i],x);
size[x]+=size[v[i]];
if(size[v[i]]>size[son[x]])son[x]=v[i];
}
}
void dfs2(int x,int y,int z){
Y[x]=z;
X[x]=++cnt[z];
for(int i=g[x];i;i=nxt[i])if(v[i]!=y&&v[i]!=son[x])dfs2(v[i],x,z+1);
if(son[x])dfs2(son[x],x,z);
}
int main(){
scanf("%d",&n);
for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
dfs(1,0);
dfs2(1,0,1);
for(i=1;i<=n;i++)printf("%d %d\n",X[i],Y[i]);
}

  

D. Decoding of Varints

按题意模拟即可。

#include<cstdio>
typedef unsigned long long ll;
int n,x;ll ans,len;
int main(){
scanf("%d",&n);
ans=0;
len=1;
while(n--){
scanf("%d",&x);
if(x<128){
ans+=1ULL*x*len;
if(ans%2==0){
printf("%llu\n",ans/2);
}else{
printf("-%llu\n",ans/2+1);
}
ans=0;
len=1;
}else{
ans+=1ULL*(x-128)*len;
len*=128;
}
}
}

  

E. Empire History

留坑。

F. Fake or Leak?

设部分终榜里排名最高的队伍为$A$,最低的为$B$,那么对于每个没出现的队伍,有两种可能:

$1.$最坏情况:封榜后一题也没有通过,检查是否比$B$还靠后。

$2.$最好情况:封榜后立即AK了,检查是否比$A$还靠前。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 1010, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m, k;
string s[N], s2[N];
char ch[N][30], ch2[N][30];
int num[N][30], num2[N][30], tim[N][30], tim2[N][30];
int solvenum[N], solvenum2[N], solvetim[N], solvetim2[N], solvelst[N], solvelst2[N];
map<string, int> mop, visit; int compare(int x, int y) // x solve y solve2
{
if(solvenum[x] > solvenum2[y]) return 1;
else if(solvenum[x] < solvenum2[y]) return -1;
if(solvetim[x] > solvetim2[y]) return -1;
else if(solvetim[x] < solvetim2[y]) return 1;
if(solvelst[x] > solvelst2[y]) return -1;
else if(solvelst[x] < solvelst2[y]) return 1;
else if(s[x] < s2[y]) return 1; //
else return -1;
} int main()
{
scanf("%d%d%d",&k, &n, &m);
mop.clear();
MS(solvelst, 0); MS(solvelst2, 0); MS(solvenum, 0); MS(solvenum2, 0); MS(solvetim, 0); MS(solvetim2, 0);
for(int i = 1; i <= n; i ++){
//scanf("%s", s[i]);
cin >> s[i];
mop[s[i]] = i;
for(int j = 1; j <= k; j ++){
scanf(" %c%d%d", &ch[i][j], &num[i][j], &tim[i][j]);
//printf("%c %d %d\n", ch[i][j], num[i][j], tim[i][j]);
if(ch[i][j] == '+'){
solvenum[i] ++;
solvetim[i] += (num[i][j] - 1) * 20 + tim[i][j];
gmax(solvelst[i], tim[i][j]);
}
}
}
visit.clear();
for(int i = 1; i <= m; i ++){
//scanf("%s", s2[i]);
cin >> s2[i];
visit[s2[i]] = 1;
for(int j = 1; j <= k; j ++){
scanf(" %c%d%d", &ch2[i][j], &num2[i][j], &tim2[i][j]);
if(ch2[i][j] == '+'){
solvenum2[i] ++;
solvetim2[i] += (num2[i][j] - 1) * 20 + tim2[i][j];
gmax(solvelst2[i], tim2[i][j]);
}
}
}
if(m == 1){
puts("Leaked");
return 0;
}
int FLAG = 1;
for(int i = 1; i <= n; i ++){
if(visit[s[i]] == 0){
int flag = 0;
if(compare(i, 1) > 0 || compare(i, m) < 0) flag = 1;
for(int j = 1; j <= k; j ++){ // AK
if(ch[i][j] == '-'){
solvenum[i] ++;
solvetim[i] += (num[i][j]) * 20 + 240;
gmax(solvelst[i], 240);
}
else if(ch[i][j] == '.'){
solvenum[i] ++;
solvetim[i] += 240;
gmax(solvelst[i], 240);
}
}
if(compare(i, 1) > 0 || compare(i, m) < 0) flag = 1;
if(!flag) FLAG = 0;
}
}
if(FLAG) puts("Leaked");
else puts("Fake");
//scanf("%d", &n);
return 0;
} /*
【trick&&吐槽】
3 3 2
crabs + 1 1 + 1 2 + 1 3
lions . 0 0 - 5 239 . 0 0
wombats . 0 0 . 0 0 . 0 0
wombats + 1 241 + 3 299 - 22 299
lions + 1 241 + 6 240 - 3 299 3 4 2
crabs + 1 1 + 1 2 + 1 3
lions . 0 0 + 5 239 . 0 0
wolves . 0 0 . 0 0 . 0 0
wombats . 0 0 . 0 0 . 0 0
crabs + 1 1 + 1 2 + 1 3
wombats . 0 0 + 2 299 . 0 0 【题意】 【分析】 【时间复杂度&&优化】 */

  

G. God of Winds

设$f[i][j]$表示$(i,j)$操作次数,那么可以表示成$A-B=C$的限制,检查是否矛盾即可。

#include<cstdio>
#include<iostream>
#include<cstdlib>
using namespace std;
typedef long long ll;
const int N=550,M=N*N;
int n,m,cnt,i,j,id[N][N],x,A,B,g[M],v[M*4],w[M*4],nxt[M*4],ed;
bool vis[M];
ll f[M];
inline void add(int x,int y,int z){
v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;
//printf("%d %d %d\n",x,y,z);
}//x - y = z
void dfs(int x,ll y){
if(vis[x]){
if(f[x]!=y){
puts("No");
exit(0);
}
return;
}
vis[x]=1;
f[x]=y;
for(int i=g[x];i;i=nxt[i]){
dfs(v[i],f[x]-w[i]);
}
}
int main(){
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)for(j=0;j<m;j++)id[i][j]=++cnt;
for(i=0;i<n;i++)for(j=0;j<m;j++){
B=id[i][j]; scanf("%d",&x);
A=id[(i-1+n)%n][j];
add(B,A,-x);
add(A,B,x); scanf("%d",&x);
A=id[i][(j-1+m)%m];
add(B,A,x);
add(A,B,-x);
}
for(i=1;i<=cnt;i++)if(!vis[i])dfs(i,0);
puts("Yes");
}

  

H. Hilarious Cooking

可行的$T$是一个区间,求出$T$的最小值和最大值即可。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 1e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
LL T, n, m;
LL a[N], b[N];
LL cnt(LL a, LL b)
{
//a should <= b
if(a > b)return 0;
if(b < 0)return 0; LL num = (b - a + 1); LL negnum = a < 0 ? abs(a) : 0;
LL sub = (1 + negnum) * negnum / 2; return (a + b) * num / 2 + sub;
}
bool check()
{
LL BOT = 0;
LL TOP = 0;
for(int i = 1; i <= m; ++i)
{
BOT += b[i];
TOP += b[i];
}
for(int i = 1; i < m; ++i)
{
LL pos_dif = a[i + 1] - a[i];
LL val_dif = abs(b[i + 1] - b[i]);
LL mx = max(b[i + 1], b[i]);
LL mn = min(b[i + 1], b[i]);
if(val_dif > pos_dif)
{
//
//puts("TOO MUCH VAL_DIF");
//
return 0;
}
if(pos_dif == 1)continue;
LL top = mx + (pos_dif - val_dif) / 2;
LL bot = mn - (pos_dif - val_dif) / 2;
if(val_dif % 2 == pos_dif % 2)
{
TOP += cnt(mx, top) + cnt(mn, top) - max(top, 0ll);
BOT += cnt(bot, mx) + cnt(bot, mn) - max(bot, 0ll);
}
else
{
TOP += cnt(mx, top) + cnt(mn, top);
BOT += cnt(bot, mx) + cnt(bot, mn);
}
TOP -= mn + mx;
BOT -= mn + mx;
} {
//1:
LL top = b[1] + (a[1] - 1);
LL bot = b[1] - (a[1] - 1);
TOP += cnt(b[1] + 1, top);
BOT += cnt(bot, b[1] - 1);
//
//printf("pretop = %lld, prebot = %lld\n", top, bot);
//
}
{
//m:
LL top = b[m] + (n - a[m]);
LL bot = b[m] - (n - a[m]);
TOP += cnt(b[m] + 1, top);
BOT += cnt(bot, b[m] - 1);
//
//printf("subtop = %lld, subbot = %lld\n", top, bot);
//
}
//
//printf("TOP = %lld, BOT = %lld\n", TOP, BOT);
//
return BOT <= T && TOP >= T;
}
int main()
{
while(~scanf("%lld%lld%lld",&T, &n, &m))
{
for(int i = 1; i <= m; ++i)
{
scanf("%lld%lld", &a[i], &b[i]);
}
puts(check() ? "Yes" : "No");
}
return 0;
} /*
【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */

  

I. Infinite Gift

将每个向量模$2$后压位高斯消元检查是否有解即可。时间复杂度$O(\frac{nk^2}{64})$。

#include<cstdio>
#include<bitset>
using namespace std;
const int N=1510;
int n,m,i,j,k,x;bitset<N>g[N];
int main(){
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
scanf("%d",&x);
if(x%2)g[i][j]=1;
}
g[i][0]=1;
}
for(i=j=1;i<=m;i++){
for(k=j;k<=n;k++)if(g[k][i])break;
if(k>n)continue;
swap(g[k],g[j]);
for(k=j+1;k<=n;k++)if(g[k][i])g[k]^=g[j];
j++;
}
for(;j<=n;j++)if(g[j][0])return puts("No"),0;
puts("Yes");
}

  

J. Judging the Trick

取一条水平线,做纵向的一维线段覆盖,若有解则找到了一组可行解。

否则注意到三角形总面积小于矩形面积,故求出水平线左右侧的三角形总面积和矩形面积,根据这个条件判断哪边存在解,然后缩小范围即可。

时间复杂度$O(n\log n\log w)$。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 1e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n;
const long double eps = 1e-11;
int sgn(long double x)
{
if(fabs(x) < eps) return 0;
return x > 0 ? 1 : -1;
}
long double sqr(long double x){return x * x;}
struct point
{
long double x, y;
point(){}
point(long double a, long double b):x(a), y(b) {}
friend point operator + (const point &a, const point &b){
return point(a.x + b.x, a.y + b.y);
}
friend point operator - (const point &a, const point &b){
return point(a.x - b.x, a.y - b.y);
}
friend bool operator == (const point &a, const point &b){
return sgn(a.x - b.x) == 0 && sgn(a.y - b.y) == 0;
}
friend point operator * (const point &a, const long double &b){
return point(a.x * b, a.y * b);
}
friend point operator * (const long double &a, const point &b){
return point(a * b.x, a * b.y);
}
friend point operator / (const point &a, const long double &b){
return point(a.x / b, a.y / b);
}
long double norm(){
return sqrt(sqr(x) + sqr(y));
}
}; long double det(const point &a, const point &b)
{
return a.x * b.y - a.y * b.x;
}
long double dot(const point &a, const point &b)
{
return a.x * b.x + a.y * b.y;
}
struct line
{
point a, b;
line(){}
line(point x, point y) : a(x), b(y) {}
}; bool PointOnSegment(point p, point s, point t)
{
return sgn(det(p - s, t - s)) == 0 && sgn(dot(p - s, p - t)) <= 0;
}
bool parallel(line a, line b)
{
return !sgn(det(a.a - a.b, b.a - b.b));
}
bool line_make_point(line a, line b, point &res)
{
if(parallel(a, b)) return false;
long double s1 = det(a.a - b.a, b.b - b.a);
long double s2 = det(a.b - b.a, b.b - b.a);
res = (s1 * a.b - s2 * a.a) / (s1 - s2);
return true;
} bool cmp(line l1, line l2)
{
return l1.a.y < l2.a.y; } long double area(point o, point a, point b)
{
return fabs((a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x));
} int m, k; point a[N][4];
line seg[N], SEG[N]; bool check(long double mid, long double &larea, long double &rarea)
{
line ll;
ll.a = point(mid, 0), ll.b = point(mid, m);
int segn = 0;
for(int i = 1; i <= k; i ++){
int num = 0; point tmp[10];
for(int j = 0; j < 3; j ++){
point res;
if(line_make_point(ll, line(a[i][j], a[i][j + 1]), res) && PointOnSegment(res, a[i][j], a[i][j + 1])){
tmp[++ num] = res;
}
}
if(num) {
long double miny = 1e9, maxy = 0;
for(int i = 1; i <= num; i ++){
gmin(miny, tmp[i].y);
gmax(maxy, tmp[i].y);
}
tmp[1].x = mid, tmp[1].y = miny;
tmp[2].x = mid, tmp[2].y = maxy;
seg[++ segn].a = tmp[1], seg[segn].b = tmp[2];
if(sgn(tmp[1].y - tmp[2].y) == 0){
if(sgn(a[i][0].x - mid) < 0 || sgn(a[i][1].x - mid) < 0 || sgn(a[i][2].x - mid) < 0)
larea += area(a[i][0], a[i][1], a[i][2]);
else rarea += area(a[i][0], a[i][1], a[i][2]);
}
else if(sgn(a[i][0].x - mid) == 0 || sgn(a[i][1].x - mid) == 0 || sgn(a[i][2].x - mid) == 0){
int lnum = 0, rnum = 0;
point pl[4], pr[4];
for(int j = 0; j < 3; j ++){
if(sgn(a[i][j].x - mid) < 0) pl[++ lnum] = a[i][j];
if(sgn(a[i][j].x - mid) > 0) pr[++ rnum] = a[i][j];
}
if(lnum == 1 && rnum == 1){
larea += area(tmp[1], tmp[2], pl[1]);
rarea += area(tmp[1], tmp[2], pr[1]);
}
else if(lnum == 1) larea += area(a[i][0], a[i][1], a[i][2]);
else rarea += area(a[i][0], a[i][1], a[i][2]);
}
else{
int lnum = 0, rnum = 0;
point pl[4], pr[4];
for(int j = 0; j < 3; j ++){
if(sgn(a[i][j].x - mid) < 0) pl[++ lnum] = a[i][j];
if(sgn(a[i][j].x - mid) > 0) pr[++ rnum] = a[i][j];
}
if(lnum == 1){
long double tt = area(pl[1], tmp[1], tmp[2]);
larea += tt;
rarea += area(a[i][0], a[i][1], a[i][2]) - tt;
}
else{
long double tt = area(pr[1], tmp[1], tmp[2]);
rarea += tt;
larea += area(a[i][0], a[i][1], a[i][2]) - tt;
}
}
}
else{
if(sgn(a[i][0].x - mid) < 0) larea += area(a[i][0], a[i][1], a[i][2]);
else rarea += area(a[i][0], a[i][1], a[i][2]);
}
}
// 合并线段
sort(seg + 1, seg + segn + 1, cmp);
long double r = 0;
for(int i = 1; i <= segn; i ++){
if(i==1&&sgn(seg[i].a.y)>0){
double xx = mid; double yy = 0;
printf("%.8f %.8f\n", xx, yy);
return 1;
}
if(sgn(seg[i].a.y - r) > 0){
double xx = mid; double yy = (seg[i].a.y+r)/2;
printf("%.8f %.8f\n", xx, yy);
return 1;
}
r = max(r,seg[i].b.y);
}
if(sgn(r-m)<0){
double xx = mid; double yy = m;
printf("%.8f %.8f\n", xx, yy);
return 1;
}
return 0;
} int main()
{
scanf("%d%d%d", &k, &n, &m);
for(int i = 1; i <= k; i ++){
for(int j = 0; j < 3; j ++){
double xx, yy;
scanf("%lf%lf", &xx, &yy);
a[i][j].x = xx; a[i][j].y = yy;
} a[i][3] = a[i][0];
}
long double l = 0, r = n;
while(1){
long double mid = (l + r) / 2;
long double larea = 0, rarea = 0;
if(check(mid, larea, rarea)){ // 可以的话在里面输出
break;
}
else{
if(larea - mid*2.0*m < -1e-6) r = mid;
else l = mid;
}
}
return 0;
} /*
【trick&&吐槽】 5 4 3
0 0 3 0 0 2
3 3 0 1 0 3
1 1 3 1 2 3
3 0 4 0 4 3
4 3 3 2 4 1 【题意】 【分析】 【时间复杂度&&优化】 */