题目链接
A - Balloon Robot
如果机器人从x开始某队需要等待的时间是t,那么如果从x+1开始,需要等待的时间就是。由此我们可以先处理出机器人从1开始的等待时间,然后排序之后依次扫过去取结果的最小值,理论的复杂度为。
但是发现,有很多点是一定不会成为最优解的。可以证明只有在p个队伍中出现过的点才有可能会成为最优解,这样总的复杂度就只有。
#include
using namespace std;
typedef long long ll;
const int maxn = 100050;
int t, n, m, p;
int a, b, pos[maxn];
ll res[maxn];
int main()
{
scanf("%d", &t);
while(t--)
{
scanf("%d%d%d", &n, &m, &p);
for(int i = 1;i <= n;i++)
scanf("%d", &pos[i]);
ll ans = 0;
for(int i = 1;i <= p;i++)
{
scanf("%d%d", &a, &b);
if(b%m <= pos[a]-1) res[i] = pos[a] - 1 - b%m;
else res[i] = m - (b%m - pos[a] + 1);
ans += res[i];
}
sort(res+1, res+p+1);
res[0] = -1;
ll cnt = ans;
for(int i = 1;i <= p;i++)
{
if(res[i] == res[i-1]) continue;
ll now = cnt - 1LL*res[i]*p + 1LL*m*(i-1);
ans = min(ans, now);
}
printf("%lld\n", ans);
}
return 0;
}
C - Crusaders Quest
直接把串hash掉暴搜即可。
#include
using namespace std;
typedef long long ll;
const int maxn = 15;
int t, ans;
char s[maxn];
map mp;
void dfs(int now, int num)
{
if(now == 0) {ans = max(num, ans); return;}
int tmp = now, no = 0;
int p[maxn];
while(tmp) {p[no++] = tmp % 4, tmp /= 4;}
int pos = 0, tp;
while(pos < no)
{
tp = pos;
while(tp < no && p[tp] == p[pos]) tp++;
int nxt = 0;
for(int i = 0;i < no;i++)
{
if(i >= pos && i < tp) continue;
nxt *= 4;
nxt += p[i];
}
if(tp - pos == 3) dfs(nxt, num+1);
else dfs(nxt, num);
pos = tp;
}
}
int main()
{
scanf("%d", &t);
mp['g'] = 1, mp['a'] = 2, mp['o'] = 3;
while(t--)
{
scanf("%s", s);
int tmp = 0;
for(int i = 0;i < 9;i++)
{
tmp *= 4;
tmp += mp[s[i]];
}
ans = 0;
dfs(tmp, 0);
printf("%d\n", ans);
}
return 0;
}
E - String of CCPC
由于第i次添加的代价是i-1且每个CCPC子串的贡献均为1,可以证明取最优解时最多添加一个字符。
预处理标记已存在的串以后扫一遍查找是否存在不冲突的子串即可。
#include
using namespace std;
typedef long long ll;
const int maxn = 200050;
int t, n;
char s[maxn];
bool vis[maxn];
int main()
{
scanf("%d", &t);
while(t--)
{
scanf("%d%s", &n, s);
memset(vis, 0, sizeof(vis));
int ans = 0;
for(int i = 0;i < n-3;i++)
{
if(s[i] == 'C' && s[i+1] == 'C' && s[i+2] == 'P' && s[i+3] == 'C')
ans++, vis[i] = 1;
}
bool flag = 0;
for(int i = 0;i < n;i++)
{
if(s[i] == 'C' && s[i+1] == 'C' && s[i+2] == 'C' && !vis[i+1])
flag = 1;
if(s[i] == 'C' && s[i+1] == 'P' && s[i+2] == 'C' && (!vis[i-1]||i==0))
flag = 1;
if(s[i] == 'C' && s[i+1] == 'C' && s[i+2] == 'P' && !vis[i])
flag = 1;
}
ans += flag;
printf("%d\n", ans);
}
return 0;
}
G - Numbers
既然要使按位或的结果最小,就尽量使较高位上全部都为0。考虑直接构造或的结果,从高位到低位贪心。对于第i位,判断m个数的所有低位全部填满是否比n大。
如果比n小,那这一位就只能填1并且n对1<
用java的BigInteger实现。
import java.util.Scanner;
import java.math.BigInteger;
public class Main
{
public static void main(String args[])
{
Scanner cin = new Scanner(System.in);
int t = cin.nextInt();
BigInteger zer = new BigInteger("0");
BigInteger one = new BigInteger("1");
BigInteger two = new BigInteger("2");
for(int i = 0;i < t;i++)
{
BigInteger n = cin.nextBigInteger();
BigInteger m = cin.nextBigInteger();
BigInteger mi = new BigInteger("1");
while(mi.subtract(one).multiply(m).compareTo(n) <= 0) mi = mi.multiply(two);
BigInteger ans = new BigInteger("0");
while(n.compareTo(zer) > 0)
{
while(mi.subtract(one).multiply(m).compareTo(n) >= 0) mi = mi.divide(two);
BigInteger tmp =mi.multiply(m);
ans = ans.add(mi);
if(tmp.compareTo(n) < 0) n = n.subtract(tmp);
else n = n.mod(mi);
}
System.out.println(ans);
}
}
}
H - Prime Set
容易想到如果两个数之和为素数,那么这两个数的奇偶性一定不同。由此可以将给出的数分成奇偶两部分,然后相加为素数的数之间连边,构成了一个二分图。
由于要求数目尽可能大,首先对这个图求最大匹配。然后根据最大匹配与k的关系来讨论。如果小于k,就从未匹配的点中再往里连。一个匹配的贡献是2,而其余点的贡献是1。
#include
using namespace std;
typedef long long ll;
const int maxn = 3050;
const int maxm = 2000050;
int t, n, m, a[maxn], linker[maxn];
bool flag[maxn], pri[maxm], vis[maxn];
vectormaze[maxn];
void init()
{
memset(pri, 0, sizeof(pri));
pri[1] = 1;
for(ll i = 2;i < maxm;i++)
{
if(pri[i]) continue;
for(ll j = i*i; j < maxm;j += i)
pri[j] = 1;
}
}
bool dfs(int u)
{
vis[u] = 1;
int len = maze[u].size();
for(int i = 0;i < len;i++)
{
int v = maze[u][i];
if(vis[v]) continue;
vis[v] = 1;
if(linker[v] == -1 || dfs(linker[v]))
{
linker[u] = v;
linker[v] = u;
return true;
}
}
return false;
}
int hungary()
{
memset(linker, -1, sizeof(linker));
int res = 0;
for(int i = 1;i <= n;i++)
{
if(flag[i] && linker[i] == -1)
{
memset(vis, 0, sizeof(vis));
if(dfs(i)) res++;
}
}
return res;
}
int main()
{
scanf("%d", &t);
init();
while(t--)
{
scanf("%d%d", &n, &m);
for(int i = 0;i < maxn;i++)
maze[i].clear();
for(int i = 1;i <= n;i++)
scanf("%d", &a[i]);
memset(flag, 0, sizeof(flag));
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
{
if(!pri[a[i]+a[j]])
{
flag[i] = flag[j] = 1;
maze[i].push_back(j);
maze[j].push_back(i);
}
}
}
int ans = hungary();
if(ans >= m) printf("%d\n", m*2);
else
{
int cnt = 0;
for(int i = 1;i <= n;i++)
{
if(flag[i] && linker[i] == -1)
cnt++;
}
ans = ans*2 + min(m - ans, cnt);
printf("%d\n", ans);
}
}
return 0;
}
L - One-Dimensional Maze
统计左边的R数目和右边的L数目取最小值即可。
M - Safest Buildings
对于一个点,下一轮安全区刷新的可行圆心区域是一个圆,和原安全区面积交最大的点就是存活概率最大的点。
为了避免精度丢失,可以将面积交转化为距离来判断。与圆心距离dis < R - 2*r的必定安全,其余的点就可以用这个距离来表示。
#include
using namespace std;
typedef long long ll;
const int maxn = 1050;
const double pi = acos(-1.0);
int t, n;
double r, R;
struct node
{
double x, y;
int id;
double s;
}e[maxn];
bool cmp(node a, node b)
{
if(fabs(a.s - b.s) < 1e-6) return a.id < b.id;
return a.s < b.s;
}
int main()
{
scanf("%d", &t);
while(t--)
{
scanf("%d%lf%lf", &n, &R, &r);
e[n+1].s = -1.0;
double tp = (R - 2*r)*(R - 2*r);
for(int i = 1;i <= n;i++)
{
scanf("%lf%lf", &e[i].x, &e[i].y);
e[i].id = i;
double dis = e[i].x*e[i].x + e[i].y*e[i].y;
if(dis < tp) e[i].s = 0;
else e[i].s = dis;
}
sort(e+1, e+1+n, cmp);
int ans = 1;
for(int i = 1;i <= n+1;i++)
{
if(fabs(e[i].s - e[1].s) > 1e-6)
{
ans = i-1;
break;
}
}
if(r == R) ans = n;
printf("%d\n", ans);
for(int i = 1;i <= ans;i++)
{
printf("%d", e[i].id);
if(i == ans) printf("\n");
else printf(" ");
}
}
return 0;
}