【比赛】gym-100753

比赛

2015 German Collegiate Programming Contest (GCPC 15) + POI 10-T3

ABCDEFGHIJKLM
**Y**ZY**YYLLLL-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. 1,此时直接输出 a n s ans ans
  2. 一个大质数,输出 2 a n s 2ans 2ans
  3. 两个不同的大质数的积,输出 4 a n s 4ans 4ans
  4. 一个大质数的平方,输出 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 n5000
然后此题是一类经典的最短路问题的应用。以集合内最小的数 X X X构建同余系。在表示出一个同余系下 0 → X − 1 0\to X-1 0X1之间的数,对其加 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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值