比赛
2015 German Collegiate Programming Contest (GCPC 15) + POI 10-T3
A | B | C | D | E | F | G | H | I | J | K | L | M |
---|---|---|---|---|---|---|---|---|---|---|---|---|
**Y | **Z | Y | **Y | Y | L | L | L | L | - | L | - | **Y |
题解
A
预处理要到达的点到所有点的最短路,然后就转换成了类旅行商问题,直接使用状压dp即可。
每个点消耗的时间可以直接从总时间内减去,写成记忆化搜索更好实现。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100010;
const int inf = 0x7fffffff;
struct gra{
int mxn;
int head[maxn], to[maxn<<1], nxt[maxn<<1], cnt;
int f[maxn<<1];
void clear(int n=0){
if(n) mxn = n;
if(cnt != 0){
for(int i = 0; i <= mxn; i ++) head[i] = 0;
cnt = 0;
}
}
void add(int a, int b, int c){
nxt[++ cnt] = head[a];
head[a] = cnt;
to[cnt] = b, f[cnt] = c;
}
}e;
struct node{
int x, v;
node(int x=0, int v=0) : x(x), v(v) {}
bool operator < (const node &t) const {return v > t.v;}
};
int n, p, m, g, t;
int pp[20], pv[20];
int dis[20][maxn];
bool vis[maxn];
priority_queue <node> q;
void dij(int S, int ct){
while(!q.empty()) q.pop();
for(int i = 0; i <= n; i ++) vis[i] = 0, dis[ct][i] = inf;
dis[ct][S] = 0, q.push(node(S, dis[ct][S]));
while(!q.empty()){
node tt = q.top(); q.pop();
if(vis[tt.x]) continue; vis[tt.x] = 1;
for(int i = e.head[tt.x]; i; i = e.nxt[i]){
int u = e.to[i];
if(!vis[u] && dis[ct][u] > dis[ct][tt.x] + e.f[i]){
dis[ct][u] = dis[ct][tt.x] + e.f[i];
q.push(node(u, dis[ct][u]));
}
}
}
}
int f[1<<15][2][16];
int count(int x){
int res = 0;
while(x) res += (x&1), x >>= 1;
return res;
}
int dfs(int st, bool use, int x, bool start){
if(st == 0) return x == 0 ? 0 : inf;
if(f[st][use][x] != -1) return f[st][use][x];
int res = inf;
if(start){
for(int i = 1; i <= p; i ++){
int v1 = use ? dfs(st, 1, i, 0) : inf, v2 = dfs(st, 0, i, 0);
if(use && v1 != inf) res = min(res, v1 + dis[i][0]);
if(v2 != inf) res = min(res, v2 + (use ? 0 : dis[i][0]));
}
}else{
if(count(st) == 1){
int v1 = use ? dfs(0, 1, 0, 0) : inf, v2 = dfs(0, 0, 0, 0);
if(use && v1 != inf) res = min(res, v1 + dis[x][0]);
if(v2 != inf) res = min(res, v2 + use ? 0 : dis[x][0]);
}else{
for(int i = 1; i <= p; i ++){
if(!(st&(1<<(i-1))) || i == x) continue;
int v1 = use ? dfs((st^(1<<(x-1))), 1, i, 0) : inf, v2 = dfs((st^(1<<(x-1))), 0, i, 0);
if(use && v1 != inf) res = min(res, v1 + dis[i][pp[x]]);
if(v2 != inf) res = min(res, v2 + (use ? 0 : dis[i][pp[x]]));
}
}
}
return f[st][use][x] = res;
}
int main(){
cin >> n >> p >> m >> g >> t;
e.clear(n);
for(int i = 1; i <= p; i ++) {
scanf("%d%d", pp+i, pv+i), g -= pv[i];
if(pp[i] == 0) p --, i --;
}
for(int i = 1, a, b, c; i <= m; i ++){
scanf("%d%d%d", &a, &b, &c);
e.add(a, b, c), e.add(b, a, c);
}
for(int i = 1; i <= p; i ++) dij(pp[i], i);
int mx = (1 << p) - 1;
memset(f, -1, sizeof(f));
if(dfs(mx, 0, 0, 1) > g){
if(dfs(mx, 1, 0, 1) > g - t){
puts("impossible");
}else puts("possible with taxi");
}else puts("possible without taxi");
return 0;
}
B
求有向图的最小路径覆盖,忘记可以转化成二分图了,想网络流的时候想到了拆点,差点YY出正解,赛后搜题解搜到了自己的博客。。。
具体做法:因为是DAG,直接转化为二分图,对原图拆点保证二分图,由Konig定理,答案是点数减去最大匹配数
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=100100;
int par[N],te,check[N],ans,head[N],n,m;
struct edge{
int v,nxt;
}e[1000010];
void add(int u,int v)
{
e[++te].v=v;
e[te].nxt=head[u];
head[u]=te;
}
int dfs(int u)
{
for (int i=head[u];i;i=e[i].nxt)
{
int v=e[i].v;
if (!check[v])
{
check[v]=1;
if (par[v]==-1||dfs(par[v]))
{
par[u]=v;
par[v]=u;
return 1;
}
}
}
return 0;
}
void hungarian()
{
memset(par,-1,sizeof(par));
for (int i=1;i<=n;i++)
{
if (par[i]==-1)
{
memset(check,0,sizeof(check));
if(dfs(i))++ans;
}
}
}
int main()
{
ans=0;
cin>>n;
for (int i=1;i<=n;++i)
{
scanf("%d", &m);
for (int j = 1, k; j <= m; ++j)
scanf("%d", &k), add(i, k + n + 1);
}
hungarian();
cout<<n-ans;
}
C
二分答案,每次使用三角形相似计算每个三角形的面积。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100010;
const int inf = 0x7fffffff;
struct point{
long double x, y;
point(long double x=0, long double y=0) : x(x), y(y) {}
point operator + (const point &t) const{return point(x+t.x, y+t.y);}
point operator - (const point &t) const{return point(x-t.x, y-t.y);}
point operator * (long double t) const{return point(x*t, y*t);}
long double operator ^ (const point &t) const{return x*t.y - y*t.x;}
long double dis(){return sqrt(x*x + y*y);}
};
long double x;
int n;
typedef vector <point> py;
long double calc(py pp){
long double res = 0;
for(int i = 1; i < pp.size(); i ++) res += pp[i] ^ pp[i-1];
res = res + (pp[0] ^ pp[pp.size()-1]);
return fabs(res) / 2;
}
long double aa[110];
int main(){
cin >> x >> n;
py pp;
for(int i = 1; i <= n; i ++){
long double x, y;
cin >> x >> y;
pp.push_back(point(x, y));
}
for(int i = 0; i < n; i ++){
aa[i] = fabs(((pp[(i-1+n)%n] - pp[i]) ^ (pp[(i+1)%n] - pp[i])) / 2);
}
long double l = 2, r = 1000, ss = calc(pp);
while(r-l >= 1e-9){
long double mid = (l+r) / 2, s = 1 / mid, res = 0;
for(int i = 0; i < n; i ++) res += aa[i] * s * s;
res = ss - res;
if(res > x * ss) r = mid;
else l = mid;
}
printf("%.10lf", (double)((l+r) / 2));
return 0;
}
D
深度优先搜索。首先按行为第一关键字,列为第二关键字进行编号。未被覆盖的编号最小的一个点,其左边一定不能被再次覆盖,所以枚举每一个没有用过的矩形能不能覆盖当前点,能的话标记覆盖,继续搜序。标记可以使用 std::bitset \text{std::bitset} std::bitset进行优化。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100010;
const int inf = 0x7fffffff;
struct node{
int x, y;
node(int x=0, int y=0) : x(x), y(y) {}
};
int w, h, c;
vector <node> v;
bitset <101> bt[101];
bitset <101> cc[101];
bool check(){
for(int i = 0; i < w; i ++) if(bt[i].count() != h) return false;
return true;
}
bool nothas(int x1, int y1, int x2, int y2){
if(x2 >= w || y2 >= h) return false;
for(int i = x1; i <= x2; i ++){
if((cc[y2-y1+1] & (bt[i] >> y1)).count() != 0) return false;
}
return true;
}
void flpt(int x1, int y1, int x2, int y2){
for(int i = x1; i <= x2; i ++){
bt[i] ^= (cc[y2-y1+1] << y1);
}
}
int nxt(int pos){
for(int i = pos; i < w*h; i ++){
int x = i/h, y = i % h;
if(bt[x][y] == 0) return i;
}
return -1;
}
bool vis[10];
bool dfs(int pos){
if(check()) return true;
for(int i = 0; i < v.size(); i ++){
if(!vis[i]){
if(nothas(pos / h, pos % h, pos / h + v[i].x - 1, pos % h + v[i].y - 1)){
flpt(pos / h, pos % h, pos / h + v[i].x - 1, pos % h + v[i].y - 1);
vis[i] = 1;
if(dfs(nxt(pos))) return true;
vis[i] = 0;
flpt(pos / h, pos % h, pos / h + v[i].x - 1, pos % h + v[i].y - 1);
}
if(nothas(pos / h, pos % h, pos / h + v[i].y - 1, pos % h + v[i].x - 1)){
flpt(pos / h, pos % h, pos / h + v[i].y - 1, pos % h + v[i].x - 1);
vis[i] = 1;
if(dfs(nxt(pos))) return true;
vis[i] = 0;
flpt(pos / h, pos % h, pos / h + v[i].y - 1, pos % h + v[i].x - 1);
}
}
}
return false;
}
int main(){
for(int i = 0; i <= 100; i ++)
for(int j = 0; j < i; j ++) cc[i].flip(j);
cin >> w >> h >> c;
for(int i = 1; i <= c; i ++){
int k, x, y;
cin >> k >> x >> y;
while(k --) v.push_back(node(x, y));
}
if(dfs(0)) puts("yes");
else puts("no");
return 0;
}
E
判断最短路是否有两条或以上,直接跑dij即可,路径数会爆 long long \text{long long} long long,注意处理。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1000010;
const ll inf = 0x7fffffffffffffffLL;
struct node{
int x;
ll v;
node(int x=0, ll v=0) : x(x), v(v) {}
bool operator < (const node &t) const{
return v > t.v;
}
};
int n, m, k;
struct gra{
int mxn;
int head[maxn], to[maxn<<1], nxt[maxn<<1], cnt;
ll f[maxn<<1];
void clear(int n=0){
if(n) mxn = n;
if(cnt != 0){
for(int i = 1; i <= mxn; i ++) head[i] = 0;
cnt = 0;
}
}
void add(int a, int b, ll c){
nxt[++ cnt] = head[a];
head[a] = cnt;
to[cnt] = b, f[cnt] = c;
}
}e;
priority_queue <node> q;
ll dis[maxn], num[maxn];
bool vis[maxn];
void dij(){
for(int i = 1; i <= n; i ++) dis[i] = inf;
dis[1] = 0, q.push(node(1, 0)), num[1] = 1;
while(!q.empty()){
node t = q.top(); q.pop();
if(vis[t.x]) continue; vis[t.x] = 1;
for(int i = e.head[t.x]; i; i = e.nxt[i]){
int u = e.to[i];
if(dis[u] >= dis[t.x] + e.f[i]){
if(dis[u] == dis[t.x] + e.f[i]){
if(num[u] + num[t.x] > 10) num[u] = 10;
else num[u] += num[t.x];
}else if(!vis[u]){
dis[u] = dis[t.x] + e.f[i];
num[u] = num[t.x];
q.push(node(u, dis[u]));
}
}
}
}
}
int main(){
scanf("%d%d%d", &n, &m, &k);
e.clear(n);
for(int i = 1, a; i <= k; i ++) scanf("%d", &a);
for(int i = 1, a, b, c; i <= m; i ++){
scanf("%d%d%d", &a, &b, &c);
e.add(a, b, c), e.add(b, a, c);
}
dij();
if(num[n] >= 2) puts("yes");
else puts("no");
return 0;
}
F
可以直接
Pollard_rho
\text{Pollard\_rho}
Pollard_rho算法进行质因数分解,复杂度
O
(
n
1
4
)
O(n^{\frac{1}{4}})
O(n41)。
不过也可以不用,我们可以先用小于
2
e
6
2e6
2e6的数对原数进行分解并统计答案为
a
n
s
ans
ans,此时剩下的数只有4种情况。
- 1,此时直接输出 a n s ans ans
- 一个大质数,输出 2 a n s 2ans 2ans
- 两个不同的大质数的积,输出 4 a n s 4ans 4ans
- 一个大质数的平方,输出 3 a n s 3ans 3ans
质数的判定用 Miller_rabin \text{Miller\_rabin} Miller_rabin
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2000010;
const int mx = 2000000;
const ll inf = 0x7fffffffffffffffll;
int pri[maxn], ct;
bool is[maxn];
ll mul(ll x, ll y, ll p){
ll res = 0;
for(x %= p; y; x = (x + x) % p, y >>= 1) if(y & 1) res = (res + x) % p;
return res;
}
ll ppow(ll x, ll y, ll p){
ll res = 1;
for(x %= p; y; x = mul(x, x, p), y >>= 1) if(y & 1) res = mul(res, x, p);
return res;
}
bool MR(ll x, ll a){
ll d=x-1,s=0,i;
while(!(d&1))d>>=1,s++;
ll t=ppow(a,d,x);
if(t==1||t==-1)return 1;
for(int i=0;i<s;++i)
{
if(t==x-1)return 1;
t=mul(t,t,x);
}
return 0;
}
bool isp(ll x){
for(int i = 1; i <= 500; i ++){
if(x==pri[i])return 1;
if(!x%pri[i])return 0;
if(x>pri[i]&&!MR(x, pri[i])) return false;
}
return true;
}
int main(){
is[1] = 1;
for(int i = 2; i <= mx; i ++){
if(!is[i]) pri[++ ct] = i;
for(int j = 1; j <= ct; j ++){
if(i * pri[j] > mx) break;
is[i * pri[j]] = 1;
if(i % pri[j] == 0) break;
}
}
ll x, res = 1;
cin >> x;
res=1;
for(int i = 1; i <= ct; i ++){
if(x % pri[i] == 0){
int ct = 0;
while(x % pri[i] == 0) x /= pri[i], ct ++;
res = res * (ct+1);
}
}
ll p=sqrt(x);
if(x == 1) printf("%lld\n", res);
else if(p*p==x)printf("%lld\n", res * 3);
else if(isp(x)) printf("%lld\n", res * 2);
else printf("%lld\n", res * 4);
return 0;
}
G
水题
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#define LL long long
#define inl inline
#define re register
#define MAXN 10100
using namespace std;
int n;
int a[2010];
int minn=100000;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
minn=min(minn,a[j]-a[i]);
if(minn>=0)printf("yes\n");
else printf("no\n");
return 0;
}
H
按照题目要求建图,连边, DFS \text{DFS} DFS遍历即可
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#define LL long long
#define inl inline
#define re register
#define MAXN 10100
using namespace std;
struct edge
{
int to;
edge *next;
}head[401];
void add(int from,int to)
{
edge *p=new edge;
p->to=to;
p->next=head[from].next;
head[from].next=p;
}
string t[401],PRO="PROGRAM",y[401][401];
int n,b[401];
bool vis[401],is[401];
bool check(string p)
{
int l=p.size();
if(l<7)return 0;
string y=p.substr(l-7,7);
return y==PRO&&p[l-8]==':'&&p[l-9]==':';
}
void dfs(int i)
{
vis[i]=1;
for(edge *p=head[i].next;p!=NULL;p=p->next)
if(!vis[p->to])dfs(p->to);
}
int ans;
int main()
{
scanf("%d",&n);
for(int u=1;u<=n;u++)
{
cin>>t[u];
is[u]=check(t[u]);
scanf("%d",&b[u]);
for(int i=1;i<=b[u];i++)
cin>>y[u][i];
}
for(int u=1;u<=n;u++)
for(int i=1;i<=b[u];i++)
for(int j=1;j<=n;j++)
if(y[u][i]==t[j])add(j,u);
for(int i=1;i<=n;i++)
if(is[i]&&!vis[i])dfs(i);
for(int i=1;i<=n;i++)ans+=(vis[i]==0);
cout<<ans;
return 0;
}
I
读懂题意,水题
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#define LL long long
#define inl inline
#define re register
#define MAXN 10100
using namespace std;
int w,s,x,y;
int t[MAXN];
int ty[MAXN][101];
int main()
{
scanf("%d%d%d%d",&w,&s,&x,&y);
for(int i=1;i<=w;i++)
for(int j=1;j<=x;j++)
scanf("%d",&ty[i][j]);
for(int i=1;i<=s;i++)
for(int j=1;j<=x;j++)
{
int u;
scanf("%d",&u);
t[j]=max(u,t[j]);
}
for(int i=1;i<=w;i++)
{
for(int j=1;j<=x;j++)
printf("%d ",min(ty[i][j],y-t[j]));
printf("\n");
}
return 0;
}
K
水题,时间有点卡,稍微优化一下质数判定的常数,加了加 register \text{register} register就过了
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#define LL long long
#define inl inline
#define re register
#define MAXN 10100
using namespace std;
LL u,l;
bool key1,key2;
bool check(LL p)
{
if(p==1)return 0;
LL lim=sqrt(p);
for(re int i=2;i<=lim;i++)
if(p%i==0)return 0;
return 1;
}
int bef[20],ind;
int aft[20];
bool check2()
{
for(int i=ind;i>=1;i--)
{
int tmp=bef[i];
if(tmp==6)tmp=9;
else if(tmp==9)tmp=6;
else if(tmp==3||tmp==4||tmp==7)return 0;
aft[i]=tmp;
}
for(int i=1;i<=ind;i++)l=l*10+aft[i];
return check(l);
}
int main()
{
cin>>u;
key1=check(u);
while(u)
{
bef[++ind]=u%10;
u/=10;
}
key2=check2();
if(key1&&key2)printf("yes\n");
else printf("no\n");
return 0;
}
M
首先这个题,数据范围给错了,实际上
n
≤
5000
n\le5000
n≤5000。
然后此题是一类经典的最短路问题的应用。以集合内最小的数
X
X
X构建同余系。在表示出一个同余系下
0
→
X
−
1
0\to X-1
0→X−1之间的数,对其加
X
X
X,即可以得到大于当前数的所有同余的数。基于此,只要求出同余系中的每一个数的最小原数字即可。于是转化为
X
X
X个点的最短路问题。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100010;
const ll inf = 0x7fffffffffffffffll;
struct node{
int x;
ll v;
node(int x=0, ll v=0) : x(x), v(v) {}
bool operator < (const node &t) const{return v > t.v;}
};
int n, k, a[maxn], b[maxn];
ll f[maxn];
bool vis[maxn];
priority_queue <node> q;
vector <node> vt;
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", a+i);
scanf("%d", &k);
for(int i = 1; i <= k; i ++) scanf("%d", b+i);
int v = a[1];
for(int i = 0; i < v; i ++) f[i] = inf;
for(int i = 1; i <= n; i ++) f[a[i] % v] = min(f[a[i] % v], 1ll * a[i]);
for(int i = 0; i < v; i ++)
if(f[i] != inf) {
vt.emplace_back(node(i, f[i]));
q.push(node(i, f[i] /= v));
}
while(!q.empty()){
node t = q.top(); q.pop();
if(vis[t.x]) continue; vis[t.x] = true;
for(int i = 0; i < vt.size(); i ++){
int u = (t.x + vt[i].x) % v;
if(!vis[u] && f[u] > (f[t.x] * v + t.x + vt[i].v) / v){
f[u] = (f[t.x] * v + t.x + vt[i].v) / v;
q.push(node(u, f[u]));
}
}
}
for(int i = 1; i <= k; i ++){
if(b[i] == 0 || f[b[i] % v] <= b[i] / v) puts("TAK");
else puts("NIE");
}
return 0;
}