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