2019 ICPC Asia Nanjing Regional

A - Hard Problem

计蒜客 - 42395

若 n = 10,可以先取:6,7,8,9,10。然后随便从1,2,3,4,5里面选一个都肯定符合题意

若 n = 9,可以先取:5,6,7,8,9,然后随便从1,2,3,4里面选一个都肯定符合题意。

所以答案就是后半部分的数量+1

#include <cstdio>
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 100000 + 5; int T, n; int main() {
scanf("%d",&T);
while(T--){
scanf("%d",&n);
if(n & 1)printf("%d\n",n/2+2);
else printf("%d\n", n/2+1);
}
return 0;
}

B.Chessboard

首先当\(n=1\)或者\(m=1\)的时候只有两种方法,从上往下刷或者从下往上刷,特判输出2。

考虑一般情况。

怎么样的粉刷策略是非法的?

当我刷着刷着凸出来一块就是非法的。

因为我只要凸出来一块,一定能导致某一对涂了色的方块之间的距离大于曼哈顿距离。

所以合法的情况就是,只能要么一次增加一行,或者增加一列。

那么从最开始的\(1\times 1\)的矩阵变成\(n\times m\)的矩阵需要\(n+m-2\)次操作,其中有\(n-1\)次是行扩张,所以答案为:\(C_{n+m-2}^{n-1}\)。

因为我们每次都是增加一行,或者增加一列,所以终点一定在四个角上,所以最后还要对答案乘以4。

这里我用\(lucas\)写的,其实也可以\(nlogn\)预处理后\(O(1)\)出结果,不过都差不多,不计较。

#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
const int maxn = 1e6+10;
typedef long long ll;
int n, m;
ll fact[maxn];
ll qmi(ll a, ll b, ll p)
{
ll res = 1; res %= p;
while(b)
{
if(b & 1) res = res*a%p;
a = a*a%p;
b >>= 1;
}
return res;
} ll C(ll n, ll m, ll p)
{
if(m > n) return 0;
return ((fact[n]*qmi(fact[m],mod-2,mod))%mod*qmi(fact[n-m],mod-2,mod)%mod);
} ll lucas(ll a, ll b, ll p)
{
if(b == 0) return 1;
return C(a%p, b%p, p) * lucas(a/p, b/p, p) % p;
} void solve()
{
scanf("%d%d", &n, &m);
if(n == 1 || m == 1)
{
puts("2");
return;
} ll ans = lucas(n+m-2, n-1, mod);
ans = (ans*4)%mod;
printf("%lld\n", ans);
} int main()
{
fact[0] = 1;
for(int i = 1; i <= (int)1e6; i++)
fact[i] = (fact[i-1]*i)%mod;
int T; scanf("%d", &T);
while(T--) solve();
return 0;
}

C - Digital Path

计蒜客 - 42397

相邻的两个点如果权值相差为1,则从小的向大的连边。然后最终连成的是一个DAG,在DAG上面按照拓扑序DP即可。

#include <cstdio>
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 1000000 + 5;
const int M = 4000005;
const int mod = 1e9 + 7;
int head[N], ver[M], nxt[M], edge[M], deg[N], tot;
int ends[N];
void add(int x, int y){
deg[y]++;
ver[++tot] = y, nxt[tot] = head[x], head[x] = tot;
}
int n, m, a[1010][1010];
ll d[N][5];// d[x][4]表示以x为终点,长度大于等于4的路径个数,d[x][3]表示长度等于3的路径个数,其他类似
inline int id(int x, int y){
return (x-1)*m+y;
}
int main() {
scanf("%d%d",&n,&m);
memset(a, 0xcf, sizeof a); //注意这里的初始化
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int x = id(i, j);
if(a[i+1][j] == a[i][j] + 1) add(x, id(i+1,j));
if(a[i][j+1] == a[i][j] + 1) add(x, id(i,j+1));
if(a[i-1][j] == a[i][j] + 1) add(x, id(i-1,j));
if(a[i][j-1] == a[i][j] + 1) add(x, id(i,j-1));
}
}
queue<int> q;
for(int i=1;i<=n*m;i++)if(deg[i] == 0)q.push(i),d[i][1] = 1;
ll res = 0;
while(q.size()){
int x = q.front();q.pop();
bool flag = true;
for(int i=head[x];i;i=nxt[i]){
int y = ver[i];
d[y][2] = (d[y][2] + d[x][1]) % mod;
d[y][3] = (d[y][3] + d[x][2]) % mod;
d[y][4] = (d[y][4] + d[x][3] + d[x][4])%mod;
if(--deg[y] == 0)q.push(y);
flag = false;
}
if(flag) res = (res + d[x][4])%mod;//如果出度为0则记录到答案中
}
printf("%lld\n",res);
return 0;
}

F - Paper Grading

计蒜客 - 42400

trie树跑dfs建树状数组套权值线段树

首先将字符串都加到Trie中,然后标记 \(f[i]\) 为每个字符串的最后一个字符所指定的Trie结点,考虑每次询问,在Trie中向下走 k 次,对于可以找到合法结点的情况,答案就是\(f[l..r]\) 中,属于该合法结点子树的个数。

到这里,先不考虑修改操作,可以把Trie按dfs序建立主席树,\(dfn[i]\) 表示第 i 号结点的dfs序,然后对每个\(f[i]\), 在第\(dfn[f[i]]\) 棵权值线段树的 \(i\) 位置的权值+1,然后每次查询子树区间中有多少个数字在\([l,r]\)的范围内。

加上修改操作,可以利用树状数组维护权值线段树,交换两个字符串的位置,也就是交换了$ f[i]$,直接在权值线段树中修改即可

#include <cstdio>
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 200000 + 5;
int n, m, L[30][30], R[30][30], cl, cr;
int f[N];
char s[N];
inline int lowbit(int x){return x & -x;}
struct SEG{
struct node{
int ls,rs;
int sum;
}t[N*200];
int rt[N], tot, maxn, res;
void update(int &rt, int l, int r, int pos, int val){
if(!rt) rt = ++tot;
t[rt].sum += val;
if(l == r)return;
int mid = l + r >> 1;
if(pos <= mid) update(t[rt].ls, l, mid, pos, val);
else update(t[rt].rs, mid+1, r, pos, val);
}
void update(int x, int pos, int v){
for(;x<=maxn;x+=lowbit(x))update(rt[x],1,n,pos,v);
}
void query(int dep, int l, int r, int ql, int qr){
if(ql > qr) return;
if(l >= ql && r <= qr){
for(int i=1;i<=cl;i++) res -= t[L[dep][i]].sum;
for(int i=1;i<=cr;i++) res += t[R[dep][i]].sum;
return;
}
int mid = l+r>>1;
if(ql <= mid){
for(int i=1;i<=cl;i++)L[dep+1][i] = t[L[dep][i]].ls;
for(int j=1;j<=cr;j++)R[dep+1][j] = t[R[dep][j]].ls;
query(dep+1,l,mid,ql,qr);
}
if(qr > mid){
for(int i=1;i<=cl;i++)L[dep+1][i] = t[L[dep][i]].rs;
for(int j=1;j<=cr;j++)R[dep+1][j] = t[R[dep][j]].rs;
query(dep+1,mid+1,r,ql,qr);
}
}
}seg;
struct Trie{
int ch[N][26], tot;
int dfn[N], cid[N], out[N], cnt;
int ins(char *s){
int p = 1, len = strlen(s);
for(int i=0;i<len;i++){
int c = s[i] - 'a';
if(!ch[p][c]) ch[p][c] = ++tot;
p = ch[p][c];
}
return p;
}
void dfs(int x){
dfn[x] = ++cnt, cid[cnt] = x;
for(int i=0;i<26;i++){
if(ch[x][i]) dfs(ch[x][i]);
}
out[x] = cnt;
}
int find(char *s, int k){
int p = 1;
for(int i=0;i<k;i++){
int c = s[i] - 'a';
if(!ch[p][c]) return -1;
p = ch[p][c];
}
return p;
}
}trie; int main() {
scanf("%d%d",&n,&m);
trie.tot = 1;
for(int i=1;i<=n;i++){
scanf("%s",s);
f[i] = trie.ins(s);
}
trie.dfs(1);
seg.maxn = trie.cnt;//maxn为权值线段树的个数
for(int i=1;i<=n;i++){
f[i] = trie.dfn[f[i]];//这里将f进一步映射到了Trie结点的dfs序上
seg.update(f[i], i, 1);
}
while(m--){
int op, l, r, k;
scanf("%d",&op);
if(op == 1){
scanf("%d%d",&l,&r);
seg.update(f[l], l, -1);
seg.update(f[r], r, -1);
seg.update(f[r], l, 1);
seg.update(f[l], r, 1);
swap(f[l], f[r]);
}else {
scanf("%s%d%d%d",s, &k, &l, &r);
int pos = trie.find(s, k);
if(pos == -1){
printf("0\n");continue;
}
int x = trie.dfn[pos]-1, y = trie.out[pos];
cl = cr = 0;
for(int j=x;j;j-=lowbit(j))L[0][++cl] = seg.rt[j];
for(int j=y;j;j-=lowbit(j))R[0][++cr] = seg.rt[j];
seg.res = 0;
seg.query(0, 1, n, l, r);
printf("%d\n",seg.res);
}
}
return 0;
}

H - Prince and Princess

计蒜客 - 42402

a = 1, b = 0, c = 0, 则 res = 0

a > 1, b = 0, c = 0 则 res = 1

a > b, b > 0, c = 0, 则最坏情况下需要问 2*b+1个人 “公主在那个房间”,一定会得到一个 b+1 次的答案

a < b, b > 0, c = 0, 则 每个 b 都可以与a回答一样的答案(因为他们的骗子),所以无法区分,也就无法得到正确结果

a > b + c, 则类似第三种的处理方式,询问 2*(b+c) +1 次 “公主在那个房间”,出现次数最多的就是答案

a < b + c, 则无解

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <map>
using namespace std;
typedef long long ll; int main() {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
if(c == 0) {
if(b == 0) {
puts("YES");
if(a == 1) puts("0");
else puts("1");
} else if(a > b) {
puts("YES");
printf("%d\n", b + b + 1);
} else {
puts("NO");
}
} else {
if(a > b + c) {
puts("YES");
printf("%d\n", b + c + b + c + 1);
} else {
puts("NO");
}
}
return 0;
}

J - Spy

计蒜客 - 42404

首先要转换为二分图带权最大匹配问题,然后用KM算法去解决。

dfs版本的KM,每递归一次只会在相等子图中增加一条边,而每次递归都是极限 \(O(M)\) 的复杂度,所以当 \(M=N^2\) 时,复杂度将达到\(O(N^4)\),

即使加了\(slack\) 数据记录最小值进行优化,那也无法避免这样的情况。

BFS版本的KM,每次BFS复杂度是 \(O(M)\) 的,但每次BFS会扩展\(O(N)\) 条边,所以总复杂度是\(O(N^3)\) 的

参考:https://blog.csdn.net/c20182030/article/details/73330556

#include <cstdio>
#include <iostream>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 605; ll n, a[N],b[N],c[N],p[N];
ll w[N][N];
ll lx[N] , ly[N];
ll match[N];
ll slack[N];
bool vy[N];
ll pre[N];
void bfs( ll k ){
ll x , y = 0 , yy = 0 , delta;
memset( pre , 0 , sizeof(pre) );
for( ll i = 1 ; i <= n ; i++ ) slack[i] = inf;
match[y] = k;
while(1){
x = match[y]; delta = inf; vy[y] = true;
for( ll i = 1 ; i <= n ;i++ ){
if( !vy[i] ){
if( slack[i] > lx[x] + ly[i] - w[x][i] ){
slack[i] = lx[x] + ly[i] - w[x][i];
pre[i] = y;
}
if( slack[i] < delta ) delta = slack[i] , yy = i ;
}
}
for( ll i = 0 ; i <= n ; i++ ){
if( vy[i] ) lx[match[i]] -= delta , ly[i] += delta;
else slack[i] -= delta;
}
y = yy ;
if( match[y] == -1 ) break;
}
while( y ) match[y] = match[pre[y]] , y = pre[y];
} ll KM(){
memset( lx , 0 ,sizeof(lx) );
memset( ly , 0 ,sizeof(ly) );
memset( match , -1, sizeof(match) );
for( ll i = 1 ; i <= n ; i++ ){
memset( vy , false , sizeof(vy) );
bfs(i);
}
ll res = 0 ;
for( ll i = 1 ; i <= n ; i++ ){
if( match[i] != -1 ){
res += w[match[i]][i] ;
}
}
return res;
} int main()
{
scanf("%lld",&n);
for(ll i=1;i<=n;i++) scanf("%lld",&a[i]);
for(ll i=1;i<=n;i++) scanf("%lld",&p[i]);
for(ll i=1;i<=n;i++) scanf("%lld",&b[i]);
for(ll i=1;i<=n;i++) scanf("%lld",&c[i]);
for(ll i=1;i<=n;i++){
for(ll j=1;j<=n;j++){
ll s=0;
for(ll k=1;k<=n;k++){
if(b[i]+c[j]>a[k]) s+=p[k];
}
w[i][j]=s;
}
}
printf("%lld\n",KM());
return 0;
}

K.Triangle:

这道题看起来比写起来难。

首先考虑在不在点在不在线上,这属于板子活,原理我也不咋懂,找了个板子抄了一下。

然后考虑几个特殊情况,\(p\)点和三角形三个顶点\(A,B,C\)重合,那么一定是对边的中点。

想到这里我们也可以发现一个很好的性质。

假设我的点落在\(AB\)上,如果点靠\(A\)更近一点,那么另一个点一定落在\(BC\)上;如果靠\(B\)近一点,那么另一个点一定落在\(AC\)上;如果落在\(AB\)中点,那么另一个点是三角形的顶点。

所以把点调整一下,让点落在\(AB\)靠近\(A\)的地方,然后二分另一个点距离\(C\)的长度,最后根据长度的比例计算一下点的坐标就可以了。

因为题目要求\(1e-6\),所以我设置\(eps=1e-8\),开\(double\),不知道有没有别的精度问题。

需要注意:提前处理出需要用的边的长度,减少库函数的调用。

#include<bits/stdc++.h>
using namespace std;
typedef double ld;
ld eps = 1e-8; struct Point{
ld x, y;
}a, b, c, p, q;
ld area;
ld ab, ac, bc, ap, bp; bool onSegment(Point Pi, Point Pj, Point Q)
{
if((Q.x - Pi.x) * (Pj.y - Pi.y) == (Pj.x - Pi.x) * (Q.y - Pi.y) //叉乘
//保证Q点坐标在pi,pj之间
&& min(Pi.x , Pj.x) <= Q.x && Q.x <= max(Pi.x , Pj.x)
&& min(Pi.y , Pj.y) <= Q.y && Q.y <= max(Pi.y , Pj.y))
return true;
else
return false;
} ld dist(Point a, Point b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
} bool check(ld mid, ld sinb)
{
ld are = sinb*(bp)*(bc-mid)/2;
if(are*2.0 >= area) return 0;
return 1;
} void solve()
{
scanf("%lf%lf%lf%lf%lf%lf%lf%lf", &a.x, &a.y, &b.x, &b.y, &c.x, &c.y, &p.x, &p.y);
//cin>>a.x>>a.y>>b.x>>b.y>>c.x>>c.y>>p.x>>p.y;
if(!onSegment(a, b, p)&&!onSegment(b, c, p)&&!onSegment(a, c, p))
{
puts("-1");
return;
} //先看看会不会在某个点上
if(p.x == a.x && p.y == a.y)
{
//输出bc中点
q.x = (b.x+c.x)/2.0;
q.y = (b.y+c.y)/2.0;
//cout << setprecision(10) << q.x << " " << q.y << endl;
printf("%.10f %.10f\n", q.x, q.y);
return;
}
else if(p.x == b.x && p.y == b.y)
{
//ac中点
q.x = (a.x+c.x)/2.0;
q.y = (a.y+c.y)/2.0;
//cout << setprecision(10) << q.x << " " << q.y << endl;
printf("%.10f %.10f\n", q.x, q.y);
return;
}
else if(p.x == c.x && p.y == c.y)
{
//ab中点
q.x = (a.x+b.x)/2.0;
q.y = (a.y+b.y)/2.0;
// cout << setprecision(10) << q.x << " " << q.y << endl;
printf("%.10f %.10f\n", q.x, q.y);
return;
} //把点弄到ab上
if(onSegment(a, b, p)) {}
else if(onSegment(b, c, p)) swap(a, c);
else if(onSegment(a, c, p)) swap(b, c); //距离b更近一点
if(dist(a, p) > dist(b, p)) swap(a, b); //开始搞
ab = dist(a, b);
ac = dist(a, c);
bc = dist(b, c);
ap = dist(a, p);
bp = dist(b, p); ld p = (ab+bc+ac)/(ld)2.0;
area = sqrt(p*(p-ab)*(p-bc)*(p-ac));
ld sina = area*2/ab/bc;
//area = ab*bc*sinb/2
//sina = area*2/ab/bc ld l = 0.0, r = bc;
while(l+eps < r)
{
ld mid = (l+r)/(ld)2.0;
if(check(mid, sina)) r = mid;
else l = mid;
} ld len = l;
//输出点
q.x = (b.x-c.x)*len/bc+c.x;
q.y = (b.y-c.y)*len/bc+c.y;
//cout << setprecision(10) << q.x << " " << q.y << endl;
printf("%.10f %.10f\n", q.x, q.y);
return;
} int main()
{
int T; scanf("%d", &T);
while(T--) solve();
return 0;
}

2019 ICPC Asia Nanjing Regional的更多相关文章

  1. 2019 ICPC Asia Nanjing Regional K. Triangle

    题目:在直角坐标系中给定 p1,p2,p3构成三角形,给定p4可能在三角形边上也可能不在, 问能不能在三角形上找出p5,使得线段p4p5,平分三角形(p4必须在三角形上).不能则输出-1. 思路:四个 ...

  2. Gym - 101981K The 2018 ICPC Asia Nanjing Regional Contest K.Kangaroo Puzzle 暴力或随机

    题面 题意:给你1个20*20的格子图,有的是障碍有的是怪,你可以每次指定上下左右的方向,然后所有怪都会向那个方向走, 如果2个怪撞上了,就融合在一起,让你给不超过5w步,让所有怪都融合 题解:我们可 ...

  3. Gym - 101981M The 2018 ICPC Asia Nanjing Regional Contest M.Mediocre String Problem Manacher+扩增KMP

    题面 题意:给你2个串(长度1e6),在第一个串里找“s1s2s3”,第二个串里找“s4”,拼接后,是一个回文串,求方案数 题解:知道s1和s4回文,s2和s3回文,所以我们枚举s1的右端点,s1的长 ...

  4. Gym - 101981G The 2018 ICPC Asia Nanjing Regional Contest G.Pyramid 找规律

    题面 题意:数一个n阶三角形中,有多少个全等三角形,n<=1e9 题解:拿到题想找规律,手画开始一直数漏....,最后还是打了个表 (打表就是随便定个点为(0,0),然后(2,0),(4,0), ...

  5. Gym - 101981I The 2018 ICPC Asia Nanjing Regional Contest I.Magic Potion 最大流

    题面 题意:n个英雄,m个怪兽,第i个英雄可以打第i个集合里的一个怪兽,一个怪兽可以在多个集合里,有k瓶药水,每个英雄最多喝一次,可以多打一只怪兽,求最多打多少只 n,m,k<=500 题解:显 ...

  6. Gym - 101981D The 2018 ICPC Asia Nanjing Regional Contest D.Country Meow 最小球覆盖

    题面 题意:给你100个三维空间里的点,让你求一个点,使得他到所有点距离最大的值最小,也就是让你找一个最小的球覆盖掉这n个点 题解:红书模板题,这题也因为数据小,精度也不高,所以也可以用随机算法,模拟 ...

  7. Gym - 101981J The 2018 ICPC Asia Nanjing Regional Contest J.Prime Game 计数

    题面 题意:1e6的数组(1<a[i]<1e6),     mul (l,r) =l × (l+1) ×...× r,  fac(l,r) 代表 mul(l,r) 中不同素因子的个数,求s ...

  8. Gym - 101981A The 2018 ICPC Asia Nanjing Regional Contest A.Adrien and Austin 简单博弈

    题面 题意:一堆有n个石子,编号从1⋯N排成一列,两个人Adrien 和Austin玩游戏,每次可以取1⋯K个连续编号的石子,Adrien先手,谁不能取了则输 题解:k==1时,显然和n奇偶相关,当k ...

  9. 2019 ICPC Asia Xuzhou Regional

    目录 Contest Info Solutions A. Cat B. Cats line up C. <3 numbers E. Multiply F. The Answer to the U ...

随机推荐

  1. 你真会看idea中的Log吗?

    在项目中提交代码时,我们时常忘了自己是否已经update代码或者push代码了,或者以为自己push,但是别人说你的代码没push,其实可以通过idea的Log日志中查看,你会发现里面有三种颜色的标签 ...

  2. LeetCode448-数组中消失的数字

    题目 给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次. 找到所有在 [1, n] 范围之间没有出现在数组中的数字. 您能 ...

  3. 简要MR与Spark在Shuffle区别

    一.区别 ①本质上相同,都是把Map端数据分类处理后交由Reduce的过程. ②数据流有所区别,MR按map, spill, merge, shuffle, sort, r educe等各阶段逐一实现 ...

  4. (十四)json、pickle与shelve模块

    任何语言,都有自己的数据类型,那么不同的语言怎么找到一个通用的标准? 比如,后端用Python写的,前端是js,那么后端如果传一个dic字典给前端,前端肯定不认. 所以就有了序列化这个概念. 什么是序 ...

  5. React中的合成事件

    React中的合成事件 React自己实现了一套高效的事件注册.存储.分发和重用逻辑,在DOM事件体系基础上做了很大改进,减少了内存消耗,简化了事件逻辑,并最大程度地解决了IE等浏览器的不兼容问题. ...

  6. django form和form组件

    form介绍: 我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来. 与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是 ...

  7. Py第一次练习,第二次练习

    有两个列表      l1 = [11,22,33]        l2 = [22,33,44] 1.获取内容相同的元素列表2.获取l1中有l2中没有的元素列表3.获取l1和l2中不同的元素列表 l ...

  8. 两种方式,花五分钟就能构建一个 Spring Boot 应用

    前言 Spring Boot 的好处自然不必多说,对于想要从事 Java 工作的朋友们来说,可谓是必学的技能. 在我看来,它的优势就是多快好省. 功能多,很多常用的能力都有集成: 接入快,简单的几行代 ...

  9. uwsgi 启动django

    1, django 官方文档可配置项如下: 2,启动django 的配置: 1,和settings.py 同级目录下新建wsgi.py  (该配置和manager.py 的配置基本是一样的) impo ...

  10. 树莓派zero 使用usb串口连接

    使用minicom连接bash$ lsusbBus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hubBus 001 Device 0 ...