2020.4.6--UCF Local Programming Contest 2017的正式赛
Problem A : Electric Bill
题目大意:进行电量分级制收费,1000kwh及以下一档收费,1000kwh以上按另一档收费,给出每个人的电量总额,问每人应支付多少钱。
思路:基础if else题,对于每个电量判断是否大于1000,再分别计算≤1000的价格和>1000的价格,最后相加并输出。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<cmath>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#include<complex>
#include<unordered_map>
#define deg(x) cout<<#x<<"="<<x<<endl;
#define fd(x) for(int i=0;i<x;i++)
#define fdd(a,x) for(int i=a;i<x;i++)
#define fx(x,n) for(int i=n-1;i>=x;i--)
#define mst(x) memset(x,0,sizeof(x));
#define fi first
#define se second
#define TB ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ok "ok"
#define HH puts("");
#define ls rt<<1
#define rs rt<<1|1
#define pb(a,b) a.push_back(b);
#define FIN freopen("1.in","r",stdin);
#define FOUT freopen("1.out","w",stdout);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<int,string> pss;
typedef unsigned long long ull;
const ll mod=1e18;
const ll inf=1e9;
const double eps=1e-8;
const double pi=acos(-1); int main(){
TB
int n,m;
cin>>n>>m;
int t;
cin>>t;
while(t--){
int k,sum=0;
cin>>k;
sum=n*min(k,1000);
if(k>1000){
sum+=m*(k-1000);
}
cout<<k<<" "<<sum<<'\n';
}
return 0;
}
Problem B : Simplified Keyboard
题目大意:给出一个字母分布图,如果两个字符串对应位置的字母全都相等,字符串属性为1,否则若两个字符串对应位置字母均相邻或相等,字符串属性为2,否则字符串属性为3. 现给出两个字符串,问这两个字符串的属性。
思路:可以先预处理每个字母的相邻关系,然后若a字符串与b字符串长度不等则属性必为3。长度相等时,遍历ab字符串并比较对应位置,并记录相等的数量及相邻的数量,最后判断输出。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<cmath>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#include<complex>
#include<unordered_map>
#define deg(x) cout<<#x<<"="<<x<<endl;
#define fd(x) for(int i=0;i<x;i++)
#define fdd(a,x) for(int i=a;i<x;i++)
#define fx(x,n) for(int i=n-1;i>=x;i--)
#define mst(x) memset(x,0,sizeof(x));
#define fi first
#define se second
#define TB ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ok "ok"
#define HH puts("");
#define ls rt<<1
#define rs rt<<1|1
#define pb(a,b) a.push_back(b);
#define FIN freopen("1.in","r",stdin);
#define FOUT freopen("1.out","w",stdout);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<int,string> pss;
typedef unsigned long long ull;
const ll mod=1e18;
const ll inf=1e9;
const double eps=1e-8;
const double pi=acos(-1); char a[5][20]={"","abcdefghi","jklmnopqr","stuvwxyz"};
bool f[203][203];
int fun(int x,int y,int q,int p){
f[a[x][y]][a[q][p]]=f[a[q][p]][a[x][y]]=1;
return 0;
}
int fun1(int x,int y){
if(!a[x][y]) return 0;
if(a[x][y+1]) fun(x,y,x,y+1);
if(a[x][y-1]) fun(x,y,x,y-1);
if(a[x+1][y+1]) fun(x,y,x+1,y+1);
if(a[x+1][y-1]) fun(x,y,x+1,y-1);
if(a[x+1][y]) fun(x,y,x+1,y);
if(a[x-1][y]) fun(x,y,x-1,y);
if(a[x-1][y+1]) fun(x,y,x-1,y+1);
if(a[x-1][y-1]) fun(x,y,x-1,y-1);
return 0;
} int main(){
TB
mst(f)
fdd(1,4) for(int j=0;j<9;j++) fun1(i,j);
int t;
cin>>t;
while(t--){
string s,b;
cin>>s>>b;
if(s.length()!=b.length()){
cout<<3<<'\n';
continue;
}
int flag=0,flag1=0;
fd(s.length()){
if(s[i]==b[i]) flag++,flag1++;
else if(f[s[i]][b[i]]) flag1++;
}
if(flag==s.length()) cout<<1<<'\n';
else if(flag1==s.length()) cout<<2<<'\n';
else cout<<3<<'\n';
}
return 0;
}
Problem C : Singin' in the Rain
题目大意:给出一个歌单顺序,每次听完上一首歌后需要通过按上一曲或下一曲直至达到下一首要听的歌,问要听完整个给出的歌单顺序,需要按多少次按钮,
思路:由于每次都必须达到并听完某首歌后,才能去听下一首歌单里的歌,所以每次的转换是互不影响的,所以只需用两个函数分别计算从当前歌跳至下一首目标歌曲时,往前跳和往后跳所需次数,取min记录,最后求和即是答案。需注意每次开始跳跃时是听完上一首歌,即是从上一首歌+1的位置开始跳跃。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<cmath>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#include<complex>
#include<unordered_map>
#define deg(x) cout<<#x<<"="<<x<<endl;
#define fd(x) for(int i=0;i<x;i++)
#define fdd(a,x) for(int i=a;i<x;i++)
#define fx(x,n) for(int i=n-1;i>=x;i--)
#define mst(x) memset(x,0,sizeof(x));
#define fi first
#define se second
#define TB ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ok "ok"
#define HH puts("");
#define ls rt<<1
#define rs rt<<1|1
#define pb(a,b) a.push_back(b);
#define FIN freopen("1.in","r",stdin);
#define FOUT freopen("1.out","w",stdout);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<int,string> pss;
typedef unsigned long long ull;
const ll mod=1e18;
const ll inf=1e9;
const double eps=1e-8;
const double pi=acos(-1); int n,m;
ll down(ll x,ll y){
x=x+1;
if(x<=y) return y-x;
y+=n;
return y-x;
}
ll up(ll x,ll y){
x=x+1;
if(y<=x) return x-y;
x+=n;
return x-y;
}
int main(){
TB
int t;
cin>>t;
while(t--){
cin>>n>>m;
ll f,g;
cin>>f;
ll sum=0;
while(--m){
cin>>g;
sum+=min(up(f,g),down(f,g));
//deg(sum)
f=g;
}
cout<<sum<<'\n';
}
return 0;
}
Problem D : Editor Navigation
题目大意:给出一个文档里的当前光标位置,和目标位置,光标移动规则按word的移动规则,问最少需要按多少次按键才可到达目标位置。
思路:从当前点出发进行bfs,直到达到目标位置停下。bfs时进行上下左右四次移动,且根据特殊规则判断落点应在何处。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<cmath>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#include<complex>
#include<unordered_map>
#define deg(x) cout<<#x<<"="<<x<<endl;
#define fd(x) for(int i=0;i<x;i++)
#define fdd(a,x) for(int i=a;i<x;i++)
#define fx(x,n) for(int i=n-1;i>=x;i--)
#define mst(x) memset(x,0,sizeof(x));
#define fi first
#define se second
#define TB ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ok "ok"
#define HH puts("");
#define ls rt<<1
#define rs rt<<1|1
#define pb(a,b) a.push_back(b);
#define FIN freopen("1.in","r",stdin);
#define FOUT freopen("1.out","w",stdout);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<int,string> pss;
typedef unsigned long long ull;
const ll mod=1e18;
const ll inf=1e9;
const double eps=1e-8;
const double pi=acos(-1); int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
int ans[125][85],s[125];
queue<pii> que;
int f; int main(){
TB
int t;
cin>>t;
while(t--){
cin>>f;
for(int i=1;i<=f;i++) cin>>s[i];
for(int i=1;i<=f;i++) fill(ans[i],ans[i]+81,inf);
int sx,sy,ex,ey;
cin>>sx>>sy>>ex>>ey;
que.push(pii(sx,sy));
ans[sx][sy]=0;
while(!que.empty()){
int x=que.front().fi,y=que.front().se;que.pop();
for(int i=0;i<4;i++){
int nx=dx[i]+x,ny=dy[i]+y;
if(nx<1||nx>f)continue;
if(i==0&&ny>s[x]){ nx++;ny=0; }
else if(i==1&&ny<0){ nx--;ny=s[nx]; }
else if((i==2||i==3)&&ny>s[nx])ny=s[nx];
if(nx<1||nx>f)continue;
if(ans[nx][ny]!=inf)continue;
ans[nx][ny]=ans[x][y]+1;
que.push(pii(nx,ny));
}
}
cout<<ans[ex][ey]<<'\n';
}
return 0;
}
Problem E: Simple Darts
题目大意:现给出一个圆形飞镖靶,分成等大小的w块扇形,且每个扇形分为3部分,半径为b以内的是靶心,半径在b到d之间的为双倍区,半径在d到s之间的为单倍区。现给出每次飞镖的落点,问最终总得分是多少。
思路:用自带函数atan或acos即可判断出落点与x轴的夹角,并可通过夹角判断出是在哪一片扇形,然后计算出落点离圆心的距离判断是靶心,双倍,单倍或靶外,最后把得分相加即可。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<cmath>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#include<complex>
#include<unordered_map>
#define deg(x) cout<<#x<<"="<<x<<endl;
#define fd(x) for(int i=0;i<x;i++)
#define fdd(a,x) for(int i=a;i<x;i++)
#define fx(x,n) for(int i=n-1;i>=x;i--)
#define mst(x) memset(x,0,sizeof(x));
#define fi first
#define se second
#define TB ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ok "ok"
#define HH puts("");
#define ls rt<<1
#define rs rt<<1|1
#define pb(a,b) a.push_back(b);
#define FIN freopen("1.in","r",stdin);
#define FOUT freopen("1.out","w",stdout);
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<string,string> pss;
typedef unsigned long long ull;
const ll mod=1e18;
const ll inf=1e9;
const double eps=1e-8;
const double pi=acos(-1); int main(){
TB
int t;
cin>>t;
while(t--){
double w,b,d,s;
cin>>w>>b>>d>>s;
int sum=0,n,flag;
cin>>n;
double x,y,tmp=2.0/w,ans,tmp1;
while(n--){
cin>>x>>y;
double len=sqrt(x*x+y*y);
if(len-b<eps){
sum+=50;
}else if(len-s>eps){
sum+=0;
}else{
if(x==0){
if(y<0) ans=1.5;
else ans=0.5;
}else{
ans=y/x;
ans=atan(ans)/pi;
if(ans<0) ans=1.0+ans;
if(y<0) ans+=1.0;
}
flag=1;tmp1=tmp;
while(ans-tmp1>eps){
tmp1+=tmp;flag++;
}
if(len-d<eps) flag*=2;
sum+=flag;
}
}
cout<<sum<<'\n';
}
return 0;
}
Problem F: Multimodal Transport
每个城市转换交通方式的费用可以看做是走了一条对应费用的边,于是可以以每个城市在每种交通方式的状态为节点建图,同城市间的点之间连一条费用为转换费用的边,再将其它边相连后求最短路就是答案
#include <bits/stdc++.h>
using namespace std; #define io ios::sync_with_stdio(false)
#define pb push_back
#define pqueue priority_queue
#define fi first
#define se second
#define ls rt<<1
#define rs rt<<1|1
#define sz(x) (int)(x).size()
#define dbg(x) cout<<#x<<" --- "<<x<<endl
#define mst(x,a) memset(x,a,sizeof(x))
#define fin(a) freopen(a,"r",stdin)
#define fout(a) freopen(a,"w",stdout)
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
ll gcd(ll a,ll b){ return b==0?a:gcd(b,a%b); }
ll qpow(ll a,ll b,ll m){ ll r=1;a%=m;for(;b;b>>=1){if(b&1)r=r*a%m;a=a*a%m;}return r; }
const int inf=0x7fffffff;
const int maxn=2e3+10;
pqueue<pii,vector<pii>,greater<pii> > que;
map<string,int> mp[5],id;
vector<pii> g[maxn];
int d[maxn];
int n,m; void dij(){
for(int i=0;i<=4*n+1;i++)d[i]=inf;
d[0]=0;que.push(pii(0,0));
while(!que.empty()){
int u=que.top().se,dis=que.top().fi;que.pop();
if(d[u]<dis)continue;
for(pii x:g[u]){
int v=x.fi,c=x.se;
if(d[v]>d[u]+c){
d[v]=d[u]+c;
que.push(pii(d[v],v));
}
}
}
} int main(){
io;
id["BOAT"]=0;id["RAIL"]=1;id["AIR"]=2;id["TRUCK"]=3;
int T;cin>>T;
while(T--){
cin>>n;
for(int i=0;i<4;i++)mp[i].clear();
for(int i=0;i<=4*n+1;i++)g[i].clear();
int tot=0;
for(int i=1;i<=n;i++){
int c;string s;cin>>s>>c;
for(int j=0;j<4;j++)mp[j][s]=++tot;
for(int j=0;j<4;j++)
for(int k=0;k<4;k++)
if(j!=k)g[mp[j][s]].pb(pii(mp[k][s],c));
}
cin>>m;
while(m--){
int c;string u,v,f;cin>>u>>v>>f>>c;
int ff=id[f];
g[mp[ff][u]].pb(pii(mp[ff][v],c));
g[mp[ff][v]].pb(pii(mp[ff][u],c));
}
string s,t;cin>>s>>t;
for(int i=0;i<4;i++)g[0].pb(pii(mp[i][s],0));
for(int i=0;i<4;i++)g[mp[i][t]].pb(pii(4*n+1,0));
dij();
cout<<d[4*n+1]<<endl;
}
return 0;
} /** 2
4
ORLANDO 10
TAMPA 15
MIAMI 5
JACKSONVILLE 10
7
TAMPA JACKSONVILLE AIR 100
MIAMI TAMPA SEA 70
JACKSONVILLE MIAMI RAIL 45
ORLANDO JACKSONVILLE TRUCK 85
TAMPA ORLANDO RAIL 10
MIAMI JACKSONVILLE SEA 15
ORLANDO MIAMI TRUCK 15
JACKSONVILLE TAMPA
2
ORLANDO 15
TAMPA 10
3
ORLANDO TAMPA AIR 7
TAMPA ORLANDO TRUCK 3
ORLANDO TAMPA RAIL 19
ORLANDO TAMPA */
Problem G: Videogame Probability
由于数据较小,于是可以c1+…+cn个物品作为单个的处理,dp[i][j]表示,前i个全部拿到需要j次尝试的概率,p[i]为第i个物品的掉落几率,cnt为总数量
dp[i][j]+=dp[i-1][j-1]*p[i]
dp[i-1][j]+=dp[i-1][j-1]*(1.0-p[i])
最后dp[cnt][j]的和就是答案
#include <bits/stdc++.h>
using namespace std; #define io ios::sync_with_stdio(false)
#define pb push_back
#define pqueue priority_queue
#define fi first
#define se second
#define ls rt<<1
#define rs rt<<1|1
#define sz(x) (int)(x).size()
#define dbg(x) cout<<#x<<" --- "<<x<<endl
#define mst(x,a) memset(x,a,sizeof(x))
#define fin(a) freopen(a,"r",stdin)
#define fout(a) freopen(a,"w",stdout)
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
ll gcd(ll a,ll b){ return b==0?a:gcd(b,a%b); }
ll qpow(ll a,ll b,ll m){ ll r=1;a%=m;for(;b;b>>=1){if(b&1)r=r*a%m;a=a*a%m;}return r; }
const int inf=0x7fffffff;
double p[55],dp[3000][10005];
vector<double> ve;
int c[55];
int n,a; int main(){
int T;cin>>T;
while(T--){
scanf("%d",&n);
ve.clear();ve.pb(0);
for(int i=1;i<=n;i++)scanf("%d%lf",&c[i],&p[i]);
for(int i=1;i<=n;i++)while(c[i]--)ve.pb(p[i]);
scanf("%d",&a);
for(int i=0;i<sz(ve);i++)
for(int j=0;j<=a;j++)
dp[i][j]=0;
dp[0][0]=1;
for(int i=1;i<sz(ve);i++)
for(int j=1;j<=a;j++){
dp[i][j]+=dp[i-1][j-1]*ve[i];
dp[i-1][j]+=dp[i-1][j-1]*(1.0-ve[i]);
}
double ans=0;
for(int i=0;i<=a;i++)ans+=dp[sz(ve)-1][i];
printf("%.3lf\n",ans);
}
return 0;
}
Problem H: Maximum Non-Overlapping Increasing Subsequences
首先暴力预处理出每个区间的lis长度,然后对于1-n每个长度区间dp即可,时间复杂度o(n4)
#include <bits/stdc++.h>
using namespace std; #define io ios::sync_with_stdio(false)
#define pb push_back
#define pqueue priority_queue
#define fi first
#define se second
#define ls rt<<1
#define rs rt<<1|1
#define sz(x) (int)(x).size()
#define dbg(x) cout<<#x<<" --- "<<x<<endl
#define mst(x,a) memset(x,a,sizeof(x))
#define fin(a) freopen(a,"r",stdin)
#define fout(a) freopen(a,"w",stdout)
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
ll gcd(ll a,ll b){ return b==0?a:gcd(b,a%b); }
ll qpow(ll a,ll b,ll m){ ll r=1;a%=m;for(;b;b>>=1){if(b&1)r=r*a%m;a=a*a%m;}return r; }
const int inf=0x7fffffff;
int dp[105][105][105];
int cnt[105][105];
int a[105],b[105];
int n; int lis(int l,int r){
int len=0;
for(int i=l;i<=r;i++){
if(b[len]<a[i])b[++len]=a[i];
else {
int p=lower_bound(b+1,b+1+len,a[i])-b;
b[p]=a[i];
}
}
return len;
} int main(){
b[0]=-inf;
int T;cin>>T;
while(T--){
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;j<=n;j++)
cnt[i][j]=lis(i,j);
mst(dp,0);
for(int i=1;i<=n;i++)dp[1][i][i]=1;
for(int kk=1;kk<=cnt[1][n];kk++){
for(int len=2;len<=n;len++){
for(int i=1;i<=n;i++){
int j=i+len-1;
if(j>n)break;
if(cnt[i][j]>=kk)dp[kk][i][j]=cnt[i][j];
for(int k=i;k<j;k++){
dp[kk][i][j]=max(dp[kk][i][j],dp[kk][i][k]+dp[kk][k+1][j]);
}
}
}
}
for(int i=1;i<=n;i++){
if(i-1)printf(" ");
printf("%d",dp[i][1][n]);
}
puts("");
}
return 0;
} /** 3
8
2 1 9 3 4 4 5 6
2
1 1
3
1 2 3 */
Problem I :Rotating Cards
有一堆数字1~n的卡片,你需要按照数字1~n的顺序将他们删去。你只能删去最上面的那张卡片,但你可以将最上面的卡片移动到最底部,也可以将最底部的卡片移动到最上面,问移动次数最少是多少。
很显然,想删去一张卡片,要么把它上面的所有卡片移动到底部,要么从底部将它拿上来。如果将这堆卡片的最上面和底部看作是首尾相连的,那么不管怎么移动,所有卡片的相对位置都是不变的。
用线段树或树状数组,区间查询,单点修改。对于删去第k张卡片,这个时候是第k-1张卡片在顶部。区间查询两张卡片之间剩余的卡片数量,就是将第k张卡片从上或从下(取决于这两张卡片一开始的相对位置)移动到顶部的移动次数a,n-k-a就是另一种移动方式的代价,取较小的那个。然后单点修改删去这张牌。
#include <cstdio> long long pi[262145],pp[233333]; void shu(int k,int l,int r)
{
if(l==r)
{
scanf("%lld",&pi[k]);
pp[pi[k]]=l; return;
}
int i=k<<1,m=(l+r)>>1;
shu(i,l,m); shu(i+1,m+1,r);
pi[k]=pi[i]+pi[i+1];
return;
} long long cha(int k,int l,int r,int a,int b)
{
if(l==a&&r==b) return pi[k];
int i=k<<1,m=(l+r)>>1;
if(a>m) return cha(i+1,m+1,r,a,b);
else if(b<=m) return cha(i,l,m,a,b);
else return cha(i,l,m,a,m)+cha(i+1,m+1,r,m+1,b);
} void gai(int k,int l,int r,int x)
{
if(l==r)
{
pi[k]=0; return;
}
int i=k<<1,m=(l+r)>>1;
if(x>m) gai(i+1,m+1,r,x);
else gai(i,l,m,x);
pi[k]=pi[i]+pi[i+1];
return;
} int main()
{
int t; scanf("%d",&t);
while(t--)
{
int n; scanf("%d",&n);
shu(1,1,n);
long long sum=0,s=pi[1];
for(int i=1,p=n; i<=n; i++)
{
long long t;
if(p<pp[i]) t=cha(1,1,n,p,pp[i]-1);
else t=cha(1,1,n,pp[i],p);
if(t*2>s) sum+=s-t;
else sum+=t;
s-=i; gai(1,1,n,pp[i]); p=pp[i];
}
printf("%lld\n",sum);
}
return 0;
}
Problem J :Multiples
求1~b中有多少数字是2~a的倍数。
只取2~a中的质数,用容斥原理解决。
对于质数k1,k2,1~b中能整除他们的数的数量分别为b/k1,b/k2。但其中有些数被重复运算,再减去b/(k1*k2),则是1~b中能整除k1或k2的数字的个数。
对于n个质数也是一样的做法。加上b除以各个质数,减去b除以任选两个质数的积,加上b除以任选三个质数的积……
求这些质数的积可以用dp来求。
#include <cstdio> int pi[233],pl[233];
long long pp[33333333];
bool pf[233],ff[33333333]; int main()
{
for(int i=3; i<=130; i+=2)
{
if(pf[i]==false) pi[++pi[0]]=i;
for(int j=1; j<=pi[0]; j++)
{
if(i*pi[j]>130) break;
pf[i*pi[j]]=true;
if(i%pi[j]==0) break;
}
}
pp[++pp[0]]=1; pl[0]=1;
for(int i=1; i<=pi[0]; i++)
{
for(int j=1; j<=pl[i-1]; j++) if(pp[j]*pi[i]<(long long)1000000000000001)
{
pp[++pp[0]]=pp[j]*pi[i];
ff[pp[0]]=not ff[j];
}
pl[i]=pp[0];
}
int t; scanf("%d",&t);
while(t--)
{
int k; long long n,sum;
scanf("%d%lld",&k,&n); sum=n/2;
for(int i=1; i<=pi[0]; i++) if(pi[i]>k) break;
else for(int j=pl[i-1]+1; j<=pl[i]; j++) if(pp[j]<=n)
{
long long p=(n/pp[j]+1)/2;
if(ff[j]==true) sum+=p;
else sum-=p;
}
printf("%lld\n",sum);
}
return 0;
}
Problem K :K-Item Shopping Spree
有n件商品,每件商品有自己的编号和价格,且每件商品的数量是无限的。问买恰好k件商品,并且价格恰好是t的购买方案数。只要编号顺序不同就认为是不同的。
首先就能想到的dp是,枚举商品数k,t2更新,维护出f[k][t]表示k件商品价格为t的方案数。但显然是超时的。
第一个很容易想到的优化,如果我们有了f[k/2][t],那么就可以直接算出f[k][t],不必一个一个枚举,这样就是n2logt,但还是不行。
还有的优化就是fft的优化。fft的原理和代码网上有很多讲得好的教程,在这里就不多讲了,我只说一下fft在这里应该怎么使用。
两个一元n次多项式相乘,朴素的方法需要n2的时间,而fft只需要nlogn。
在这题中,将方案数看作是系数,将价格看作是次方。这样更新的过程就可以看作是两个一元多次的多项式相乘。比如有a种方案价格x,b种方案价格y,两者更新后就是a*b种方案价格x+y,就像是两个数字相乘。
所以最后时间复杂度为nlognlogt。关于空间的优化,第一维的k是可以去掉的。
// Arup Guha
// 8/22/2017
// Solution to 2017 UCF Locals Problem: K Shop #include <iostream>
#include <cmath> using namespace std; // Constants
const double PI = 3.14159265358979;
const long long MOD = 997LL;
const int MAX = 50000;
const int len = (1<<17); // Globals.
int n;
int* order;
double* cosarr;
double* sinarr;
int* poly; void fft(double* real, double* img, bool invert);
long long* multiply(int* a, int* b); int* scale(long long* arr);
int* mypow(int* poly, int exp); int main() { // Building order array for FFT.
order = new int[2];
order[0] = 0;
order[1] = 1;
int orderlen = 2; // Just iterate building next array from previous.
while (orderlen != len) {
int* tmp = new int[orderlen<<1];
for (int i=0; i<orderlen; i++) {
tmp[2*i] = order[i];
tmp[2*i+1] = order[i] + orderlen;
}
delete [] order;
order = tmp;
orderlen <<= 1;
} // Pre-calculate.
cosarr = new double[len];
for (int i=0; i<len; i++) cosarr[i] = cos(i*2*PI/len);
sinarr = new double[len];
for (int i=0; i<len; i++) sinarr[i] = sin(i*2*PI/len); int numCases;
cin >> numCases; // Process each case.
for (int loop=0; loop<numCases; loop++) { // Read in the polynomial.
poly = new int[len];
for (int i=0; i<len; i++) poly[i] = 0;
int n;
cin >> n;
for (int i=0; i<n; i++) {
double price;
cin >> price;
poly[(int)(100*price + .1)]++;
} // Read in exponent and number of queries.
int exp, numQ;
cin >> exp >> numQ; // Exponentiate our polynomial.
int* res = mypow(poly, exp); // Now handle the queries.
for (int i=0; i<numQ; i++) {
double price;
cin >> price;
cout << res[(int)(100*price + .1)] << endl;
}
cout << endl; // Clean up.
delete [] res;
if (exp > 1) delete [] poly;
} // Clean up.
delete [] order;
delete [] cosarr;
delete [] sinarr;
return 0;
} // Raises poly to the exp using our MOD and lopping off appropriate terms.
int* mypow(int* poly, int exp) { // Done.
if (exp == 1) return poly; // Get the time savings here.
if (exp%2 == 0) {
int* halfway = mypow(poly, exp/2);
long long* res = multiply(halfway, halfway);
if (exp > 2) delete [] halfway;
return scale(res);
} // Slow way, never runs twice in a row.
int* rest = mypow(poly, exp-1);
long long* res = multiply(rest, poly);
delete [] rest;
return scale(res);
} // Scales the polynomial arr - lops off terms past MAX and does MOD.
int* scale(long long* arr) {
int* res = new int[len];
for (int i=0; i<=MAX; i++) res[i] = (int)(arr[i]%MOD);
for (int i=MAX+1; i<len; i++) res[i] = 0;
delete [] arr;
return res;
} void fft(double* real, double* img, bool invert) { // Re-order numbers to be in fft ordering for combination.
double* tmp = new double[len];
for (int i=0; i<len; i++) tmp[i] = real[order[i]];
for (int i=0; i<len; i++) real[i] = tmp[i];
delete [] tmp; tmp = new double[len];
for (int i=0; i<len; i++) tmp[i] = img[order[i]];
for (int i=0; i<len; i++) img[i] = tmp[i];
delete [] tmp; // size is the size of each grouping (when we combine up) in fft.
for (int size=2; size<=len; size<<=1) { // Set up angle and halfway pt.
int half = (size>>1);
int skip = len/size;
int sign = !invert ? 1 : -1; // i is the starting index for this group, j indicates pairs.
for (int i=0; i<len; i+=size) {
double aReal = 1, aImg = 0;
int index = 0;
for (int j=0; j<half; j++) {
double fReal = real[i+j], fImg = img[i+j];
double sReal = real[i+j+half]*cosarr[index] - img[i+j+half]*sinarr[index];
double sImg = img[i+j+half]*cosarr[index] + real[i+j+half]*sinarr[index];
real[i+j] = fReal + sReal;
img[i+j] = fImg + sImg;
real[i+j+half] = fReal - sReal;
img[i+j+half] = fImg - sImg;
index = (index+sign*skip+len)%len;
}
}
} // For inverse transformation, we divide by n (my len).
if (invert) {
for (int i=0; i<len; i++) {
real[i] /= len;
img[i] /= len;
}
}
} long long* multiply(int* a, int* b) { // Copy a.
double* reala = new double[len];
for (int i=0; i<len; i++) reala[i] = a[i];
double* imga = new double[len];
for (int i=0; i<len; i++) imga[i] = 0.0; // Copy b.
double* realb = new double[len];
for (int i=0; i<len; i++) realb[i] = b[i];
double* imgb = new double[len];
for (int i=0; i<len; i++) imgb[i] = 0.0; // Transform into points.
fft(reala, imga, false);
fft(realb, imgb, false); // Store product pts here.
double* realPts = new double[len];
double* imgPts = new double[len]; // Multiply points.
for (int i=0; i<len; i++) {
realPts[i] = reala[i]*realb[i] - imga[i]*imgb[i];
imgPts[i] = reala[i]*imgb[i] + realb[i]*imga[i];
} // Now, convert these points back to a polynomial.
fft(realPts, imgPts, true); // Finally copy over into a result array without doubles.
long long* res = new long long[len];
for (int i=0; i<len; i++)
res[i] = (long long)(realPts[i]+.5+1e-9); delete [] reala;
delete [] imga;
delete [] realb;
delete [] imgb;
delete [] realPts;
delete [] imgPts; return res;
}
2020.4.6--UCF Local Programming Contest 2017的正式赛的更多相关文章
- 04.06 UCF Local Programming Contest 2017
A.Electric Bill 题意:简单计算题,超过1000部分额外算 1 #include<stdio.h> 2 int main(){ 3 int money1,money2; 4 ...
- 2020.3.28-ICPC训练联盟周赛,选用试题:UCF Local Programming Contest 2016
A.Majestic 10 签到题. #include<iostream> #include<cstdio> #include<cstring> #include& ...
- UCF Local Programming Contest 2016 J题(二分+bfs)
题目链接如下: https://nanti.jisuanke.com/t/43321 思路: 显然我们要采用二分的方法来寻找答案,给定一个高度如果能确定在这个高度时是否可以安全到达终点,那我们就可以很 ...
- 03.28,周六,12:00-17:00,ICPC训练联盟周赛,选用试题:UCF Local Programming Contest 2016正式赛。
A. Majestic 10 题意:三个数均大于10则输出"triple-double",如果两个数大于10则输出"double-double",如果一个大于1 ...
- 03.21 ICPC训练联盟周赛:UCF Local Programming Contest 2018正式赛
B Breaking Branches 题意:两个人比赛折枝,谁剩下最后1,无法折出整数即为输 思路:树枝长n,若是奇数,则Bob胜出,若是偶数,则Alice胜出,且需要输出1: 1 #include ...
- The North American Invitational Programming Contest 2017 题目
NAIPC 2017 Yin and Yang Stones 75.39% 1000ms 262144K A mysterious circular arrangement of black st ...
- ACM International Collegiate Programming Contest, Tishreen Collegiate Programming Contest (2017)- K. Poor Ramzi -dp+记忆化搜索
ACM International Collegiate Programming Contest, Tishreen Collegiate Programming Contest (2017)- K. ...
- 2016-2017 CT S03E05: Codeforces Trainings Season 3 Episode 5 (2016 Stanford Local Programming Contest, Extended) E
链接:http://codeforces.com/gym/101116 学弟写的,以后再补 #include <iostream> #include <algorithm> # ...
- 2016-2017 CT S03E05: Codeforces Trainings Season 3 Episode 5 (2016 Stanford Local Programming Contest, Extended) J
链接:http://codeforces.com/gym/101116 题意:给出n个点,要求一个矩形框将(n/2)+1个点框住,要面积最小 解法:先根据x轴选出i->j之间的点,中间的点(包括 ...
随机推荐
- Springboot 日志、配置文件、接口数据如何脱敏?老鸟们都是这样玩的!
一.前言 核心隐私数据无论对于企业还是用户来说尤其重要,因此要想办法杜绝各种隐私数据的泄漏.下面陈某带大家从以下三个方面讲解一下隐私数据如何脱敏,也是日常开发中需要注意的: 配置文件数据脱敏 接口返回 ...
- 查看局域网内所有的主机名、MAC地址和IP地址
查看所有 IP at MAC $ arp -a ? (10.125.49.187) at 18:81:e:eb:ef:c0 on en0 ifscope [ethernet] ? (10.125.50 ...
- RDS导入注意事项
1)导入文件大小不超过100M,支持格式有CSV.SQL.ZIP 2)sql文件需注释如下内容: SET @@SESSION.SQL_LOG_BIN=0 ; SET @@GLOBAL.GTID_PUR ...
- 基于flex布局的header
一.如图 二.思路 1.定义header,设置宽为100%,高为60px,设置绝对定位,使其为漂浮层.在header里添加container,宽设置为版心宽度,并且设置flex布局. 2.在conta ...
- Docker宿主机管理
不需要每次输入sudo 在当前用户的用户目录.bashrc中增加以下内容,此后使用docker命令时不需要每次都增加sudo. echo 'sudo usermod -aG docker $USER' ...
- Linux系列(38) - 源码包安装(2)
安装前准备 安装C语言编译器"gcc" yum -y install gcc --c 源码包语言编译器 下载源码包 安装注意事项 源代码保存位置:/usr/local/src/ 软 ...
- mysql 优化的相关配置:总结中...
centos 为例:mysql 怎么获取配置参数信息: /etc/my.cnf; /etc/myql/my.cnf/; 家目录:或者指定目录:作用域 客户端:全局 set global 会话 set[ ...
- javascript 定时器 timer setTimeout setInterval (js for循环如何等待几秒再循环)
实现一个打点计时器,要求1.从 start 到 end(包含 start 和 end),每隔 100 毫秒 console.log 一个数字,每次数字增幅为 12.返回的对象中需要包含一个 cance ...
- Java对象构造
关于对象构造的一些认识. 默认域初始化 如果在构造器中没有显示地给域赋予初值,那么就会被自动地赋予默认值:数值为0,布尔值为false,对象引用为null.然而,这显然是不安全的,在一个null引用上 ...
- P3352-[ZJOI2016]线段树【dp】
正题 题目链接:https://www.luogu.com.cn/problem/P3352 题目大意 \(n\)个数字的一个序列,每次随机选择一个区间让这个区间所有数等于这个区间的最大值,重复\(q ...