1. 程式人生 > >2018 icpc 南京網路賽

2018 icpc 南京網路賽

題目:連結

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;
}