2018 icpc 南京网络赛

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/albertluf/article/details/82291234

题目:链接

A. An Olympian Math Problem

输出n-1即可(女朋友猜的)。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll fac[103];
int main()
{
    ll n,T;cin>>T;
    while(T--)
    {
        cin>>n;
        cout<<n-1<<endl;
    }
    return 0;
}

B. The writing on the wall

枚举矩形的右下角,设为(x,y),若以(x,y)为右下角,设y这一行小于x且离x最近的一个空地坐标为L,

则以(x,y)为右下角可以形成宽度为1的矩形(x-L)个,以(x,y)为右下角可以形成宽度为2的矩形数量为

y行与y-1行的x-L取个min。遍历一遍宽度即可。虽说理论上是10亿的复杂度,可能判题机跑的比较快吧。

也可能数据较水。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+10;
vector<int>G[maxn];
int n,m,k;
int L[maxn],iter[maxn],len[maxn];
int main()
{
    int T,cas=0;scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i=0;i<=n;i++) G[i].clear();
        while(k--)
        {
            int x,y;scanf("%d%d",&x,&y);
            G[y].push_back(x);
        }
        for(int i=1;i<=m;i++)
        {
            len[i]=G[i].size();
            sort(G[i].begin(),G[i].end());
            iter[i]=0;L[i]=0;
        }
        ll ans=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(iter[j]<len[j]&&G[j][iter[j]]==i)
                {
                    L[j]=i;iter[j]++;
                }
            }
            for(int j=1;j<=m;j++)
            {
                int mi=2e9;
                for(int k=j;k>=1;k--)
                {
                    mi=min(mi,i-L[k]);
                    ans+=mi;
                    if(mi==0) break;
                }
            }
        }
        printf("Case #%d: %lld\n",++cas,ans);
    }
    return 0;
}

 E. AC Challenge

状压dp,对于每个状态判断是否合法(即若i在集合中,则i所依赖的题目也要在集合中),

若此状态合法,则用它的上一步状态来更新此状态。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=22;
ll a[maxn],b[maxn];
ll dp[1<<21];
vector<int>G[maxn];
int main()
{
    for(int i=0;i<(1<<21);i++) dp[i]=-1e18;
    dp[0]=0;
    int n;scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        int Q;
        scanf("%lld%lld%d",&a[i],&b[i],&Q);
        while(Q--)
        {
            int tmp;scanf("%d",&tmp);
            G[i].push_back(tmp-1);
        }
    }
    for(int s=1;s<(1<<n);s++)
    {
        bool bb=0;
        for(int i=0;i<n;i++)
        {
            if(!(s>>i&1)) continue;
            for(int j=0;j<G[i].size();j++)
                if(!(s>>G[i][j]&1)) bb=1;
            if(bb) break;
        }
        if(bb) continue;
        for(int i=0;i<n;i++)
        {
            if(!(s>>i&1)) continue;
            int t=0,S=s;
            while(S)
            {
                if(S&1) t++;
                S/=2;
            }
            dp[s]=max(dp[s],dp[s^(1<<i)]+a[i]*t+b[i]);
        }
    }
    printf("%lld\n",dp[(1<<n)-1]);
    return 0;
}

G. Lpl and Energy-saving Lamps

对于当前的s从左到右找到第一个小于等于s的a[i],让后将a[i]个灯泡换成新的,a[i]改为inf。

然后灯泡数量s=s-a[i]。线段树维护即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,k,Q,s;
int a[maxn],tree[maxn<<2],ans1[maxn],ans2[maxn];
void build(int l,int r,int rt)
{
    if(l==r)
    {
        tree[rt]=a[l];
        return;
    }
    int mid=(l+r)/2;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    tree[rt]=min(tree[rt<<1],tree[rt<<1|1]);
}
int query(int C,int l,int r,int rt)
{
    if(l==r)
    {
        s-=a[l];
        return l;
    }
    int mid=(l+r)/2;
    if(tree[rt<<1]<=C) return query(C,l,mid,rt<<1);
    else return query(C,mid+1,r,rt<<1|1);
}
void update(int L,int l,int r,int rt)
{
    if(l==r)
    {
        tree[rt]=2e9;
        return;
    }
    int mid=(l+r)/2;
    if(L<=mid) update(L,l,mid,rt<<1);
    else update(L,mid+1,r,rt<<1|1);
    tree[rt]=min(tree[rt<<1],tree[rt<<1|1]);
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    build(1,n,1);
    s=0;
    for(int i=1;i<=100000;i++)
    {
        s+=k;
        ans1[i]=ans1[i-1];
        while(s>=tree[1])
        {
            ans1[i]++;
            int id=query(s,1,n,1);
            update(id,1,n,1);
        }
        ans2[i]=s;
        if(tree[1]==2e9)
        {
            for(int j=i+1;j<=100000;j++)
            {
                ans1[j]=ans1[i];
                ans2[j]=ans2[i];
            }
            break;
        }
    }
    scanf("%d",&Q);
    while(Q--)
    {
        int t;scanf("%d",&t);
        printf("%d %d\n",ans1[t],ans2[t]);
    }
    return 0;
}

J. Sum

把每个数拆成素数相乘。

设x=(a[1]^b[1])*(a[2]^b[2])*(a[3]^b[3])....

其中a[i]为质数,b[i]为指数,

然后令x=n*m,

n=(a[1]^b1[1])*(a[2]^b1[2])*(a[3]^b1[3])...

m=(a[1]^b2[1])*(a[2]^b2[2])*(a[3]^b2[3])...

则有b[1]=b1[1]+b2[1],b[2]=b1[2]+b2[2],b[3]=b1[3]+b2[3]...

问题转化成有几种合法方法分配b1,b2两个数组。

对于x的所有b[i],若存在b[i]>2,则一定无法合理安排。

因为b1[i]和b2[i]中一定有一个大于2,这样n和m至少有

一个数字含有一个完全平方数的约数。

假设b1[i]>2,则一定有n=t*a[i]^2,因此n不合法。

若b[i]=2,则只有b1[i]=1,b2[i]=1,一种选择。

若b[i]==1,则可以b1[i]=0,b2[i]=1或b1[i]=1,b2[i]=0。

代码:

#include<cstdio>
#include<iostream>
using namespace std;
#define ll long long
const int maxn=20000005;
int a[maxn],b[maxn];
bool vis[maxn];
int p[maxn],q[maxn],pp[maxn];
int sum[maxn];
void init()
{
    int len=0;
    a[1]=1;
    for(int i=2;i<maxn;i++)
    {
        if(!vis[i]) p[len++]=i;
        for(int j=0;j<len&&1LL*i*p[j]<1LL*maxn;j++)
        {
            vis[i*p[j]]=1;
            q[i*p[j]]=p[j];
            if(i%p[j]==0) break;
        }
    }
    sum[1]=1;
    for(int i=2;i<maxn;i++)
    {
        if(!vis[i]) a[i]=2;
        else
        {
            int x=i,k=0;
            while(x%q[i]==0)
            {
                x/=q[i];k++;
                if(k>2) break;
            }
            if(k>2) a[i]=0;
            else if(k==2) a[i]=a[x];
            else a[i]=2*a[x];
        }
        sum[i]=sum[i-1]+a[i];
    }
}
int main()
{
    init();
    int T;scanf("%d",&T);
    while(T--)
    {
        int n;scanf("%d",&n);
        printf("%d\n",sum[n]);
    }
    return 0;
}

 L. Magical Girl Haze

把每个点拆成k+1个点建立分层图,对于第i个点,拆成i,i+n,i+2*n...i+k*n

若原图中i~j有一条权值为x的边,则

add_edge(i,j,x),add_edge(i+n,j+n,x).....add_edge(i+k*n,j+k*n,x)

add_edge(i,j+n,0),add_edge(i+n,j+2*n,0)....add_edge(i+(k-1)*n,j+k*n,0)

每向上走一层相当于走了一条权值为0的边,

假设走了(i,j+n,0)相当于从第0层走到了第一层,而把i~j这条边的权值改为0。

最后输出1~(k+1)*n的最短路即可。

代码:

#include <bits/stdc++.h>
using namespace std;

const int mn = 1400010, mm = 5000010;
const long long inf = 1e18;

int n, m, k;

int num;
int from[mm], to[mm], nx[mm], fr[mm];
long long cost[mm];
void addedge(int a, int b, long long c)
{
	for (int i = 0; i <=k; i++)
	{
		num++;
		from[num] = a + i * n;
		to[num] = b + i * n;
		cost[num] = c;
		nx[num] = fr[a + i * n];
		fr[a + i * n] = num;
	}
	for (int i = 0; i < k ; i++)
	{
		num++;
		from[num] = a + i * n;
		to[num] = b + (i + 1) * n;
		cost[num] = 0;
		nx[num] = fr[a + i * n];
		fr[a + i * n] = num;
	}
}

struct node
{
	int id;
	long long w;
	friend bool operator < (struct node a, struct node b)
	{
		return a.w > b.w;
	}
} dis[mn];
priority_queue<node>q;

void dijkstra(int a)
{
	for (int i = 1; i < mn; i++)
	{
		dis[i].id = i;
		dis[i].w = inf;
	}

	dis[a].w = 0;
	q.push(dis[a]);

	while (!q.empty())
	{
		node cd = q.top();
		q.pop();

		for (int i = fr[cd.id]; i != -1; i = nx[i])
		{
			int v = to[i];
			if (dis[v].w > dis[cd.id].w + cost[i])
			{
				dis[v].w = dis[cd.id].w + cost[i];
				q.push(dis[v]);
			}
		}
	}
}

int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		num = 0;
		memset(fr, -1, sizeof fr);

		scanf("%d %d %d", &n, &m, &k);
		while (m--)
		{
			int a, b;
			long long c;
			scanf("%d %d %lld", &a, &b, &c);
			addedge(a, b, c);
		}

		dijkstra(1);
		printf("%lld\n", dis[k * n + n].w);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/albertluf/article/details/82291234