Nordic Collegiate Programming Contest 2016
A:
从前往后记录每次操作后变化的点。
然后从后往前维护并查集。
#include<bits/stdc++.h>
using namespace std;
const int N = 1000 ,Q = 1e4+7;
int G[N][N],fa[N*N];
int dir[4][2]={ {1,0},{-1,0},{0,1},{0,-1} };
int n,m,q,ans;
vector<int> op[Q];
int id(int x,int y) { return x*m+y; }
int f(int x){ return x==fa[x]?x:fa[x]=f(fa[x]); }
void Union(int x,int y)
{
x=f(x);
y=f(y);
if(x==y) return ;
--ans;
fa[x]=y;
}
bool check(int x,int y)
{
if(x<0||y<0||x>=n||y>=m) return false;
if(G[x][y]) return false;
return true;
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
ans=n*m;
for(int i=0;i<q;++i)
{
int x1,x2,y1,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
--x1;--y1;--x2;--y2;
if(x1==x2)
{
for(int j=y1;j<=y2;++j)
if(!G[x1][j])
{
--ans;
G[x1][j]=1;
op[i].push_back(id(x1,j));
}
}
else
{
for(int j=x1;j<=x2;++j)
if(!G[j][y1])
{
--ans;
G[j][y1]=1;
op[i].push_back(id(j,y1));
}
}
}
int L = n*m;
for(int i=0;i<L;++i) fa[i]=i;
for(int i=0;i<n;++i)
for(int j=0;j<m;++j)
if(!G[i][j])
for(int k=0;k<4;++k)
{
int x=i+dir[k][0];
int y=j+dir[k][1];
if(check(x,y)) Union(id(i,j),id(x,y));
}
vector<int> Ans;
for(int i=q-1;i>=0;--i)
{
Ans.push_back(ans);
for(int k : op[i])
{
int x=k/m;
int y=k%m;
G[x][y]=0;
++ans;
for(int j=0;j<4;++j)
{
int tx=dir[j][0]+x;
int ty=dir[j][1]+y;
if(check(tx,ty)) Union(id(x,y),id(tx,ty));
}
}
}
reverse(Ans.begin(),Ans.end());
for(int k : Ans) printf("%d\n",k);
return 0;
}
B:
只有三种操作:在末尾添加一个字符,去除末尾的一个字符,自动补全。
将所有的字典串放在Trie上,每种操作可以描绘Trie上的一条边。
然后做一次bfs。
每次查询沿着Trie查询到LCP,答案等于最后那个点距离根的距离再加上串长-LCP长。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+7;
int d[N],vis[N];
struct Trie
{
int nxt[N][26],L,root,fail[N],s[N],top;
vector<int> adj[N];
int newnode()
{
for(int i=0;i<26;++i)
nxt[L][i]=-1;
return L++;
}
void init()
{
L=0;
root=newnode();
}
void addedge(int u,int v)
{
adj[u].push_back(v);
adj[v].push_back(u);
}
void insert(char buf[],int len)
{
int now=root;top=0;
for(int i=0;i<len;++i)
{
if(nxt[now][buf[i]-'a']==-1)
{
nxt[now][buf[i]-'a'] = newnode();
s[top++]=nxt[now][buf[i]-'a'];
}
addedge(now,nxt[now][buf[i]-'a']);
now=nxt[now][buf[i]-'a'];
}
int des=now;
for(int i=0;i<top;++i) adj[s[i]].push_back(des);
}
void bfs()
{
queue<int> q;
d[0]=0;
vis[0]=1;
q.push(0);
while(!q.empty())
{
int u=q.front();q.pop();
vis[u]=true;
for(int v : adj[u])
{
if(!vis[v])
{
vis[v]=1;
d[v]=d[u]+1;
q.push(v);
}
}
}
}
int query(char buf[],int len)
{
int now=root;
for(int i=0;i<len;++i)
{
if(nxt[now][buf[i]-'a']==-1) return d[now]+len-i;
now=nxt[now][buf[i]-'a'];
}
return d[now];
}
}t;
int n,m;
char str[N];
int main()
{
scanf("%d%d",&n,&m);
t.init();
for(int i=0;i<n;++i)
{
scanf("%s",str);
int len=strlen(str);
t.insert(str,len);
}
t.bfs();
for(int i=0;i<m;++i)
{
scanf("%s",str);
int len=strlen(str);
printf("%d\n",t.query(str,len));
}
return 0;
}
C:
根据花色和点数的要求枚举每种排序结果写出<运算符,一共有
4!×24
种。
答案等于
n−LIS
。
#include<bits/stdc++.h>
using namespace std;
int fuck[128],gg[128],p[4],s,n;
struct Node
{
int c,x;
Node(){}
Node(int c,int x):c(c),x(x){}
bool operator < (const Node& b) const
{
if(c!=b.c) return p[c]<p[b.c];
if((s>>c)&1) return x<b.x;
return x>b.x;
}
}ipt[52],dp[52];
int LIS(Node* arr, int len){
int k=0;
for(int i=0;i<len;++i){
int idx=upper_bound(dp,dp+k,arr[i])-dp;
dp[idx]=arr[i];
if(idx==k)++k;
}
return k;
}
int main()
{
scanf("%d",&n);
for(char i='2';i<='9';++i) fuck[i]=i-'2';
fuck['T']=8;
fuck['J']=9;
fuck['Q']=10;
fuck['K']=11;
fuck['A']=12;
gg['s']=0;
gg['h']=1;
gg['d']=2;
gg['c']=3;
for(int i=0;i<n;++i)
{
char s[10];
scanf("%s",s);
ipt[i].c=gg[s[1]];
ipt[i].x=fuck[s[0]];
}
for(int i=0;i<4;++i) p[i]=i;
int ans = 52 ;
while(1)
{
for(s=0;s<16;++s)
ans=min(ans,n-LIS(ipt,n));
if(!next_permutation(p,p+4)) break;
}
printf("%d\n",ans);
return 0;
}
H:
因为要求严格递减,所以每种数字仅能在水平长度中出现一次。
对于长为
u
,宽为
这样其他的度数都给垂直长度做贡献。
对于我们描绘出的这个无向图,它的每一个连通块要么是一棵树,要么是一棵树+一条边。
如果是一棵树+一条边,可以形成一条环。
假设另一个图,一开始只有一条环,那么将这个环描述成答案中一层一层的矩形,那么一定是连续的,每当在这个图中加入一个点和一条边,那么新增的矩形所处的位置是确定的,画一下就知道肯定不能仅加入一条边。
对于仅有一棵树的情况,可以允许一个点没有出度,因此我们允许点权最大的那个点没有出度,这样可以最大地加到答案上。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 250007;
int x[N],y[N],p[N*2],notree;
vector<int> adj[N*2],pt;
bool vis[N*2];
void dfs(int u,int pre)
{
vis[u]=true;
for(int v : adj[u])
{
if(v==pre) continue;
if(!vis[v]||!notree)
{
pt.push_back(u);
pt.push_back(v);
}
if(!vis[v]) dfs(v,u);
else notree=true;
}
}
int main()
{
int n,m=0;
scanf("%d",&n);
for(int i=0;i<n;++i)
{
scanf("%d%d",&x[i],&y[i]);
p[m++]=x[i];
p[m++]=y[i];
}
sort(p,p+m);
m=unique(p,p+m)-p;
for(int i=0;i<n;++i)
{
x[i]=lower_bound(p,p+m,x[i])-p;
y[i]=lower_bound(p,p+m,y[i])-p;
adj[x[i]].push_back(y[i]);
adj[y[i]].push_back(x[i]);
}
ll ans=0;
for(int i=0;i<m;++i)
{
if(vis[i]) continue;
notree=false;
pt.clear();
dfs(i,-1);
sort(pt.begin(),pt.end());
int mx=0;
for(int j=0;j<pt.size();++j)
{
if(pt[j]==pt[j-1]&&j>0) continue;
int c=1;
for(int k=j+1;k<pt.size()&&pt[k]==pt[k-1];++k) ++c;
ans+=(ll)(c-1)*p[pt[j]];
mx=max(mx,p[pt[j]]);
}
if(!notree) ans+=mx;
}
printf("%lld\n",ans);
return 0;
}
K:
蓝书原题。原题是有SPJ的。
这题在计蒜客上没人过的原因是 计蒜客没加SPJ 。
#include<bits/stdc++.h>
using namespace std;
typedef long double ld;
const ld eps=1e-10;
const ld PI=acos((ld)(-1.0));
struct Point
{
ld x,y;
Point(ld x=0,ld y=0) : x(x),y(y) {}
};
typedef Point Vector;
Vector operator + (Vector a, Vector b) { return Vector(a.x+b.x, a.y+b.y); }
Vector operator - (Vector a, Vector b) { return Vector(a.x-b.x, a.y-b.y); }
Vector operator * (Vector a, ld p) { return Vector(a.x*p, a.y*p); }
Vector operator / (Vector a, ld p) { return Vector(a.x/p, a.y/p); }
bool operator < (const Point& a, const Point &b)
{
return a.x<b.x||(a.x==b.x&&a.y<b.y) ;
}
int dcmp(ld x)
{
if(fabs(x)<eps) return 0; else return x<0 ? -1 : 1;
}
bool operator == (const Point &a, const Point &b)
{
return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
}
ld operator * (Vector a, Vector b) { return a.x*b.x+a.y*b.y; }
ld operator ^ (Vector a, Vector b) { return a.x*b.y-b.x*a.y; }
ld length(Vector a) { return sqrt(a*a); }
//求P到线段AB的距离,A和B是线段的端点。
ld distanceToSegment(Point P, Point A, Point B)
{
if(A==B) return length(P-A);
Vector v1=B-A,v2=P-A,v3=P-B;
if(dcmp(v1*v2)<0) return length(v2);
else if(dcmp(v1*v3)>0) return length(v3);
else return fabs(v1^v2) / length(v1);
}
const int N = 1e5+7;
int T,n,m;
Point pa[N],pb[N];
ld MIN;
Point read_point()
{
int x,y;
scanf("%d%d",&x,&y);
return Point(x,y);
}
void update(Point P, Point A, Point B)
{
MIN=min(MIN,distanceToSegment(P,A,B));
}
int main()
{
MIN=1e8;
scanf("%d",&n);
for(int i=0;i<n;++i) pa[i]=read_point();
scanf("%d",&m);
for(int i=0;i<m;++i) pb[i]=read_point();
ld LA=0,LB=0;
for(int i=1;i<n;++i) LA+=length(pa[i]-pa[i-1]);
for(int i=1;i<m;++i) LB+=length(pb[i]-pb[i-1]);
int sa=0,sb=0;
Point PA=pa[0],PB=pb[0];
ld v=LA;
while(sa<n-1&&sb<m-1)
{
ld la=length(pa[sa+1]-PA);
ld lb=length(pb[sb+1]-PB);
ld T=min(la/v,lb/v);
Vector va=(pa[sa+1]-PA)/la*T*v;
Vector vb=(pb[sb+1]-PB)/lb*T*v;
update(PA,PB,PB+vb-va);
PA=PA+va;
PB=PB+vb;
if(PA==pa[sa+1]) ++sa;
if(PB==pb[sb+1]) ++sb;
}
printf("%.15f\n",(double)MIN);
return 0;
}