总结:都是水题. 难度在DIV2 C以下.
Problem B
水题.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e2+5,inf=0x3f3f3f3f;
int main()
{
int T,n;
cin>>T;
while(T--)
{
int lx=inf,rx=-inf,uy=inf,dy=-inf;
int x;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
for(int j=1;j<=8;j++)
{
scanf("%d",&x);
if(j%2)
lx=min(lx,x),rx=max(rx,x);
else
uy=min(uy,x),dy=max(dy,x);
}
}
cout<<(rx-lx)*(dy-uy)<<endl;
}
return 0;
}
Problem F 暴力
题意:给出两个串s1,s2 每次从其中一个串的开头选出一个字符 选n+m次结束.要求:把生成不同的串从小到大输出,n,m<=8.
直接dfs暴力即可.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e2+5;
char a[N],b[N];
vector<string> v;
int n,m;
void dfs(int x,int y,string s)
{
if(x==n&&y==m)
{
v.push_back(s);
return;
}
if(x<n)
dfs(x+1,y,s+a[x]);
if(y<m)
dfs(x,y+1,s+b[y]);
}
int main()
{
int T;
cin>>T;
while(T--)
{
v.clear();
scanf("%s%s",a,b);
n=strlen(a),m=strlen(b);
dfs(0,0,"\0");
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
for(int i=0;i<v.size();i++)
cout<<v[i]<<endl;
cout<<endl;
}
return 0;
}
Problem J 暴力打表
题意:给出数b.问第一个比b大的素数并且其二进制为回文的数为多少? b<=2^21.
先求出2e6内的素数,然后O(sz*20)生成是回文的素数表. 在表中找到第一个比b大的即可.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e6+5,inf=0x3f3f3f3f,M=3e6;
bool vis[M+5];
int pri[M+5],pn=0;
vector<int> a;
bool check(int x)
{
int b[30],sz=0;
while(x)
b[++sz]=x%2,x/=2;
for(int i=1;i<=sz/2;i++)
if(b[i]!=b[sz-i+1])
return false;
return true;
}
void init()
{
for(int i=2;i<=M;i++)
{
if(!vis[i])
{
pri[pn++]=i;
for(int j=i+i;j<=M;j+=i)
vis[j]=1;
}
}
// cout<<pn<<endl;
for(int i=0;i<pn;i++)
{
if(check(pri[i]))
a.push_back(pri[i]);
}
}
void fun(int x)
{
int b[30],sz=0;
while(x)
b[++sz]=x%2,x/=2;
for(int i=sz;i>=1;i--)
printf("%d",b[i]);
printf("\n");
}
char b[30];
int main()
{
int T,n;
init();
while(scanf("%s",b)!=EOF)
{
int c=0;
for(int i=0;b[i];i++)
c=c*2+b[i]-'0';
int pos=lower_bound(a.begin(),a.end(),c)-a.begin();
fun(a[pos]);
}
return 0;
}
Problem G 暴力
题意:题意:知道两个串s1,s2的长度为n,m 每次从其中一个串的开头选出一个字符 选n+m次结束.
问总共能生成多少个串(可以有相同的) n,m<=1e4.
暴力 f[n][m]为长度为n,m的串能生成多少个串 f[n][m]=f[n-1][m]+f[n][m-1].
正解: n+m的串 把第一个串随机放入n+m个位置中,剩下m个位置第二个串的元素依次填入即可. C(n+m,n)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e4+2,inf=0x3f3f3f3f,mod=1e9+7;
int f[N][N];
void init()
{
f[0][0]=1;
for(int i=1;i<N;i++)
f[i][0]=f[0][i]=1;
for(int i=1;i<N;i++)
{
for(int j=1;j<N;j++)
f[i][j]=(f[i-1][j]+f[i][j-1])%mod;
}
}
int main()
{
int T;
init();
scanf("%d",&T);
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
printf("%d\n",f[n][m]);
}
return 0;
}
Problem B 贪心暴力
题意:n个硬币,第i个硬币价值v[i].选硬币:价值为x的硬币要么不选 要么全选.
当选的总钱数>=M时 最少需要多少种类硬币.(最小数量相同输出总钱数大的方案.)
n,v[i]<=1e6.M<=1e9.
首先种类要求最少.二元组F(a,b)选种类b能获得的钱a,从大到小排序即可.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int> ii;
const int N=1e6+2,inf=0x3f3f3f3f,mod=1e9+7;
ll b[N];
vector<ii> a;
vector<int> ans;
bool cmp(ii a,ii b)
{
if(a.first==b.first)
return a.second>b.second;
return a.first>b.first;
}
int main()
{
int T,n,m;
scanf("%d",&T);
while(T--)
{
ll sum=0;
int x;
memset(b,0,sizeof(b));
ans.clear();
a.clear();
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&x),b[x]++,sum+=x;
if(sum<m)
{
printf("Impossible\n");
continue;
}
for(int i=1;i<N;i++)
{
if(b[i])
a.push_back(ii(i*b[i],i));
}
sort(a.begin(),a.end(),cmp);
ll res=0;
for(int i=0;i<a.size();i++)
{
res+=a[i].first;
ans.push_back(a[i].second);
if(res>=m)
break;
}
sort(ans.begin(),ans.end());
for(int i=0;i<ans.size();i++)
printf("%d ",ans[i]);
printf("\n");
}
return 0;
}
Problem D 二分 最大值最小化
题意:给出长度为n的序列a,一个区间的价值为:该区间的最大值*该区间长度.
问把序列a分成k个区间 其区间最大值最小为多少? n,a[i]<=1e6,k<=1e5,
最大值最小化 二分答案,O(N)判定是否能分成<=k个区间即可.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int> ii;
const int N=1e6+2,inf=0x3f3f3f3f,mod=1e9+7;
int n,k;
ll a[N];
bool check(ll lim)
{
ll cnt=0;
int r=1;
for(int l=1;l<=n;l=r)
{
ll mx=0;
while(r<=n)
{
mx=max(mx,a[r]);
ll len=r-l+1;
//if(lim==10)
// cout<<l<<' '<<r<<' '<<len*mx<<endl;
if(len*mx>lim)
break;
r++;
}
cnt++;
}
return cnt<=k;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
ll mx=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%I64d",&a[i]),mx=max(mx,a[i]);
ll l=mx,r=2e16;
while(l<=r)
{
ll m=l+r>>1;
if(check(m))
r=m-1;
else
l=m+1;
}
printf("%I64d\n",r+1);
}
return 0;
}
Problem E 01-Tire水
题意:长度为n的序列a,Q次询问
每次询问给出一个数x,找到a[p]^x > a[i]^x(i=1..n) 若有多个a[p] 输出p下标最小的.
n,Q<=3e5,a[i]<=1e9.
对于询问,也就是在序列a中找到p使得a[p]^x最大的即可,Trie从高位走到低位即可.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int> ii;
const int N=2e5+2,inf=0x3f3f3f3f,mod=1e9+7,M=33;
int ch[N*33][2],sz,num[N*33];
map<ll,ll> pos;
void init()
{
sz=1;
memset(ch[0],0,sizeof(ch[0]));
}
void insert(ll x)
{
int u=0;
for(int i=32;i>=0;i--)
{
int c=(x>>i)&1;
if(!ch[u][c])
{
memset(ch[sz],0,sizeof(ch[sz]));
ch[u][c]=sz++;
}
u=ch[u][c];
}
num[u]=x;
}
void query(ll x)
{
int u=0;
for(int i=32;i>=0;i--)
{
int c=(x>>i)&1;
if(ch[u][c^1])
u=ch[u][c^1];
else
u=ch[u][c];
}
printf("%d\n",pos[num[u]]);
}
int main()
{
int T,n,Q;
ll x;
scanf("%d",&T);
while(T--)
{
init();
pos.clear();
scanf("%d%d",&n,&Q);
for(int i=1;i<=n;i++)
{
scanf("%I64d",&x);
if(!pos[x])
pos[x]=i;
insert(x);
}
while(Q--)
{
scanf("%d",&x);
query(x);
}
printf("\n");
}
return 0;
}
Problem H
题意:给出n,(n1,c1),(n2,c2).<=2e9.
找到一对(x,y)满足 n[1]*x+n[2]*y=n && c[1]*x+c[2]*y 尽量小.
由exgcd: ax+by=d d|gcd(a,b)
容易求出(x,y)的通解, x=x1+kb,y=y1-ka
如何满足c[1]*x+c[2]*y 尽量小.?
暴力遍历通解 水过了....
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int> ii;
const int N=2e5+2,inf=0x3f3f3f3f,mod=1e9+7,M=33;
ll a,b,c1,c2,n,x,y;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
int r=exgcd(b,a%b,x,y),t;
t=x,x=y,y=t-a/b*y;
return r;
}
int main()
{
while(cin>>n&&n)
{
scanf("%I64d%I64d%I64d%I64d",&c1,&a,&c2,&b);
ll d=exgcd(a,b,x,y);
if(n%d)
{
printf("failed\n");
continue;
}
x=x*n/d;
y=y*n/d;
ll tx=x;
x=((x%b)+b)%b;
ll k=(tx-x)/b;
y+=k*a;
ll res=x*c1+y*c2;
ll px=x,py=y;
ll rx=x,ry=y;
for(ll k=0;k<=1000;k++)
{
x=px+k*b;
y=py-k*a;
if(x>=0&&y>=0)
{
if(x*c1+y*c2<res)
res=x*c1+y*c2,rx=x,ry=y;
}
else
break;
}
printf("%I64d %I64d\n",rx,ry);
}
return 0;
}
Problem I
题意:有n个传送站(x,y,r) 每个传送站可以传送到它半径r范围内的任意一个传送站.需要时间2s.
人可以上下左右走单位1的距离.或着到传送站传送
起点为(sx,sy),终点为(ex,ey) -1e9<=x,y<=1e9,0<=r<=1e9 n<=1e2.问从起到到终点需要的最短时间.
把起点和终点也看成传送站,设d[i][j] 表示第i个传送站到第j个传送站需要的时间(中间节点编号为0).
根据半径和坐标建图.跑一遍Floyd(或者SPFA)即可 注意起点终点不能传送.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e2+5;
const double eps=1e-10;
struct node{
double x,y;
double r;
}a[N];
int n;
ll d[N][N];
double dis(int i,int j)
{
double x1=a[i].x,y1=a[i].y;
double x2=a[j].x,y2=a[j].y;
return sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}
void floyd()
{
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
}
}
}
int main()
{
int T;
cin>>T;
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lf%lf%lf",&a[i].x,&a[i].y,&a[i].r);
++n;
scanf("%lf%lf",&a[n].x,&a[n].y),a[n].r=0;
++n;
scanf("%lf%lf",&a[n].x,&a[n].y),a[n].r=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
d[i][j]=abs(a[i].x-a[j].x)+abs(a[i].y-a[j].y);
if(dis(i,j)<=a[i].r&&i!=n-1&&i!=n&&j!=n&&j!=n-1)
d[i][j]=min(d[i][j],2ll);
}
}
floyd();
printf("%I64d\n",d[n-1][n]);
}
return 0;
}
Problem A
题意:n人比赛.n-1轮:每一轮从剩下的人当中,随机选出两个人进行比赛,输的人淘汰.
已知a[i][j] 第i个人打赢第j个人的概率.
n<=20.问第i个人成为冠军的概率(i=[1..n]).
n<=20 暴力? 好像做不了. 状压? 把淘汰的人看成1,则第i个人为冠军的状态为11110111.
转移状态.知道状态后枚举下一个被淘汰的人x 在枚举x被谁淘汰,在乘以选这两个人概率即可. O(n^2 * 2^n).
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=21;
double a[N][N],f[1<<N];
int main()
{
int T,cas=0;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
scanf("%lf",&a[i][j]);
memset(f,0,sizeof(f));
f[0]=1;
for(int i=0;i<(1<<n);i++)
{
double cnt=0;
for(int k=0;k<n;k++)
if(!((i>>k)&1))
cnt++;
cnt=(cnt*(cnt-1))/2.0;
for(int j=0;j<n;j++)
{
if((i>>j)&1)
continue;
for(int k=0;k<n;k++)
{
if(k==j)
continue;
if((i>>k)&1)
continue;
f[i|(1<<j)]+=f[i]*a[k][j]*1.0/cnt;
}
}
}
printf("Case %d:",++cas);
for(int i=0;i<n;i++)
printf(" %.6lf",f[((1<<n)-1)^(1<<i)]);
printf("\n");
}
return 0;
}