PROBLEM A D E F H I J ARE INCLUDED
题目链接:https://odzkskevi.qnssl.com/cb170c7f26e08e74eda57aef92fbde52?v=1491702860
Problem A. Area 51
题意:
给出N各点,求视角能按给定顺序看到各点的合法区间。
思路:
可以发现,对于每一个点重合处,视角中点序会发生变化,其变化就是相应两点交换顺序。故可以从 -INF 开始模拟所有区间的视角,同时检查其是否符合题意。注意精度问题。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef struct Node{
char ch;
double x,y;
bool operator <(const Node &a)const{
if(a.y==y){
return a.x>x;
}
return a.y<y;
}
}Node;
typedef struct Xpos{
int x,y;
double pos;
bool operator <(const Xpos &a)const{
if(pos==a.pos){
if(x==a.x)
return y>a.y;
return x>a.x;
}
return a.pos>pos;
}
}Xpos;
const int MAXN=105;
const double INF=1e8;
const double eps=1e-6;
char str[MAXN];
int n,tot,pos[MAXN],num[MAXN];
Node node[MAXN];
Xpos xpos[MAXN*MAXN];
double get(int i,int j){
double x1=node[i].x,y1=node[i].y;
double x2=node[j].x,y2=node[j].y;
return x1-(x1-x2)/(y1-y2)*y1;
}
void getxpos(){
tot=-1;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(node[i].y==node[j].y) continue;
xpos[++tot].pos=get(i,j);
xpos[tot].x=i;xpos[tot].y=j;
}
}
sort(xpos,xpos+tot+1);
Xpos end;end.pos=INF;end.x=end.y=0;
xpos[++tot]=end;
return ;
}
bool check(){
for(int i=0;i<n;i++)
if(node[num[i]].ch!=str[i]) return 0;
return 1;
}
int main(){
freopen("area.in","r",stdin);
freopen("area.out","w",stdout);
while(scanf("%d",&n)!=-1){
getchar();
scanf("%s",str);
getchar();
for(int i=0;i<n;i++){
scanf("%c %lf %lf",&node[i].ch,&node[i].x,&node[i].y);
getchar();
}
sort(node,node+n);
getxpos();
for(int i=0;i<n;i++){
pos[i]=i;
num[i]=i;
}
double st=-INF;
vector <pair<double,double> >ans;
for(int i=0;i<=tot;i++){
if(xpos[i].pos-st>=eps&&check()){
ans.push_back(make_pair(st,xpos[i].pos));
}
st=xpos[i].pos;
if(xpos[i].x<xpos[i].y){
swap(num[pos[xpos[i].x]],num[pos[xpos[i].y]]);
swap(pos[xpos[i].x],pos[xpos[i].y]);
}
}
int len=ans.size();
printf("%d\n",len);
for(int i=0;i<len;i++){
double x=ans[i].first,y=ans[i].second;
if(x==-INF) printf("* ");
else printf("%.6lf ",x);
if(y==INF) printf("*");
else printf("%.6lf",y);
if(i!=len-1) printf(" ");
else printf("\n");
}
return 0;
}
}
Problem D. Double Patience
题意:
有9组牌,每组4张,两张牌的大小一样时可以消掉,每次只能消掉最上面的牌,然后一个在玩的时候如果有多种消法,就会随机选择其中一种,问他将所有牌都消掉的几率是多少。
思路:
状压DP
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#define eps 1e-8
using namespace std;
int n5[10],a[10][5],b[10],w,num1,num2;
double dp[1953125];
string h;
vector<int> hh;
int main()
{
freopen("double.in","r",stdin);
freopen("double.out","w",stdout);
for(int i=0;i<=9;i++)
n5[i]=pow(5,i)+eps;
for(int i=1;i<=9;i++)
{
for(int j=1;j<=4;j++)
{
cin>>h;
a[i][j]=h[0];
}
}
memset(dp,0,sizeof(dp));
dp[1953124]=1;
for(int i=1953124;i>=0;i--)
{
if(dp[i]==0)
continue;
w=i;
for(int j=0;j<=8;j++)
{
b[j]=w%5;
w/=5;
}
hh.clear();
for(int j=0;j<=7;j++)
{
for(int k=j+1;k<=8;k++)
{
if(b[j]==0||b[k]==0)
continue;
if(a[j+1][b[j]]!=a[k+1][b[k]])
continue;
hh.push_back(i-n5[j]-n5[k]);
}
}
w=hh.size();
for(int j=0;j<w;j++)
dp[hh[j]]+=dp[i]/w;
}
printf("%.6lf\n",dp[0]+eps);
return 0;
}
Problem E. Exploring Pyramids
题意:
给出一棵多叉树,每个结点的任意两个子结点都有左右之分。从根结点开始,每次尽量往左走,走不通了就回溯,把遇到的字母顺序记录下来,可以得到一个序列。如图所示的序列均为 ABABABA 。给定一个序列,问有多少棵树与之对应。
思路:
DP
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#define mod 1000000000
using namespace std;
int main()
{
freopen("exploring.in","r",stdin);
freopen("exploring.out","w",stdout);
string a;
int dp[305][305],len;
while(cin>>a)
{
memset(dp,0,sizeof(dp));
len=a.size();
if(len%2==0)
{
cout<<0<<endl;
continue;
}
for(int i=0;i<len;i++)
dp[i][i]=1;
for(int i=2;i<len;i+=2)
{
for(int l=0;l<=len-i-1;l++)
{
int r=l+i;
if(a[l]==a[r])
{
dp[l][r]=dp[l+1][r-1];
for(int k=l+1;k<r;k++)
if(a[k]==a[l])
dp[l][r]=(dp[l][r]+(long long)dp[l+1][k-1]*dp[k][r])%mod;
}
}
}
cout<<dp[0][len-1]<<endl;
}
return 0;
}
Problem F. Feel Good
题意:
给你一个序列,让你求其中一个子序列使得这个子序列和乘以这个子序列中最小值后最大。
思路:
枚举每个位置,将这个位置视作为区间的最小值,找到左右方向比这个位置的值还小的位置。这样一来就确定了区间。
区间的和利用前缀和维护即可。
找区间左右端点利用线段树查询即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e6+100;
#define ls l,mid,rt*2
#define rs mid+1,r,rt*2+1
#define mi (l+r)/2
#define sf l,r,rt
const int INF=0x3f3f3f3f;
const long long inf=0x3f3f3f3f3f3f3f3f;
long long n,a[MAXN],minn[MAXN*4],sum[MAXN],ansl,ansr,v,posl,posr,st,en,ans1,ans2;
long long fans;
void push_up(int l,int r,int rt){
minn[rt]=min(minn[rt*2],minn[rt*2+1]);
return ;
}
void build(int l,int r,int rt){
if(l==r){
minn[rt]=a[l];
return ;
}
int mid=mi;
build(ls);
build(rs);
push_up(sf);
}
void queryl(int l,int r,int rt){
if(st>r||en<l||posr!=n+1) return ;
if(minn[rt]>=v) return ;
if(l==r){
ansl=minn[rt];
posr=l;
return ;
}
int mid=mi;
queryl(ls);
queryl(rs);
return ;
}
void queryr(int l,int r,int rt){
if(st>r||en<l||posl!=0) return ;
if(minn[rt]>=v) return ;
if(l==r){
ansl=minn[rt];
posl=r;
return ;
}
int mid=mi;
queryr(rs);
queryr(ls);
return ;
}
int main(){
freopen("feelgood.in","r",stdin);
freopen("feelgood.out","w",stdout);
while(scanf("%d",&n)!=-1){
fans=-INF;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]>fans){
fans=a[i];
ans1=ans2=i;
}
}
sum[0]=0;
for(int i=1;i<=n+1;i++)
sum[i]=sum[i-1]+a[i];
build(1,n,1);
for(int i=1;i<=n;i++){
v=a[i];
ansl=INF;
ansr=INF;
st=i+1;
en=n;
posr=n+1;
queryl(1,n,1);
st=1;
en=i-1;
posl=0;
queryr(1,n,1);
long long temp=a[i]*(sum[posr-1]-sum[posl]);
posl++;posr--;
if(temp>fans){
fans=temp;
ans1=posl;
ans2=posr;
}
}
cout<<fans<<endl;
cout<<ans1<<' '<<ans2<<endl;
}
}
Problem H. Hardwood Cutting
题意:
给出一个 N * M 的块,要将不同的区块切开,但只能从边缘切,问最终能切成几块。
思路:
先把图转化一下将每个点分开,后将切点 push 入队。
对于每个切点标记其四个方向,从四个方向进行图切。
在切的过程中会产生新的可切点也 push 入队。
显然的,当从一点开始的切线切到断点时,这条切线上的每个点都是新的切点。
切好图后,dfs 统计联通块的个数即可。
代码:
//切图可以通过 debug() 查看
#include <bits/stdc++.h>
using namespace std;
typedef struct Node{
int x,y;
Node(int xx=0,int yy=0):x(xx),y(yy){};
}Node;
char maze[50][50];
bool vis[50][50][4],viss[50][50];
int n,m;
int dirx[]={0,-1,0,1};
int diry[]={-1,0,1,0};
queue <Node> que;
void debug(){
for(int i=0;i<=n;i++){
for(int j=0;j<=m;j++){
printf("%c",maze[i][j]);
}
printf("\n");
}
puts(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
}
void ini(){
n*=2;m*=2;
memset(vis,0,sizeof(vis));
memset(maze,0,sizeof(maze));
memset(viss,0,sizeof(viss));
getchar();
for(int i=0;i<=n;i++){
for(int j=0;j<=m;j++){
if(i%2==0||j%2==0)
maze[i][j]='#';
else
scanf("%c",&maze[i][j]);
}
if(i%2) getchar();
}
for(int i=0;i<=n;i++) maze[i][0]=maze[i][m]='$';
for(int i=0;i<=m;i++) maze[0][i]=maze[n][i]='$';
return ;
}
void cut(int x,int y,int dir){
if(vis[x][y][dir]) return ;
int stx=x,sty=y;
int step=0;
bool color=0;
while(!color){
vis[x][y][dir]=1;
x+=dirx[dir];y+=diry[dir];
if(x<0||y<0||x>n||y>m) break;
step++;
if(dir%2==0){
if(maze[x-1][y]!=maze[x+1][y]){
maze[x][y]='$';
x+=dirx[dir];y+=diry[dir];
if(maze[x][y]=='$'){
color=1;
}else{
maze[x][y]='$';
}
}else{
break;
}
}else{
if(maze[x][y-1]!=maze[x][y+1]){
maze[x][y]='$';
x+=dirx[dir];y+=diry[dir];
if(maze[x][y]=='$'){
color=1;
}else{
maze[x][y]='$';
}
}else{
break;
}
}
}
if(color){
while(step--){
stx+=2*dirx[dir];sty+=2*diry[dir];
for(int i=0;i<4;i++)
if(!vis[stx][sty][i]){
que.push(Node(stx,sty));
break;
}
}
}
return ;
}
void bfs(){
while(!que.empty()) que.pop();
for(int i=0;i<=n;i+=2){
que.push(Node(i,0));
que.push(Node(i,m));
}
for(int i=2;i<m;i+=2){
que.push(Node(0,i));
que.push(Node(n,i));
}
Node temp;
while(!que.empty()){
temp=que.front();que.pop();
for(int i=0;i<4;i++)
cut(temp.x,temp.y,i);
}
}
void dfs(int x,int y){
if(viss[x][y]) return ;
viss[x][y]=1;
for(int i=0;i<4;i++){
if(maze[x+dirx[i]][y+diry[i]]!='$')
dfs(x+dirx[i]*2,y+diry[i]*2);
}
}
int solve(){
int ans=0;
for(int i=1;i<=n;i+=2){
for(int j=1;j<=m;j+=2){
if(!viss[i][j]){
ans++;
dfs(i,j);
}
}
}
return ans;
}
int main(){
freopen("hardwood.in","r",stdin);
freopen("hardwood.out","w",stdout);
while(scanf("%d %d",&n,&m)!=-1){
ini();
bfs();
//debug();
cout<<solve()<<endl;
}
}
Problem I. IP Networks
题意:
给定一些IP地址,求出子网掩码和子网内最小IP。
思路:
http://www.cnblogs.com/farewell-farewell/articles/5459415.html
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int main()
{
freopen("ip.in","r",stdin);
freopen("ip.out","w",stdout);
int a,n,zw[40],num[40],s;
while(cin>>n)
{
memset(num,-1,sizeof(num));
for(int i=1;i<=32;i++)
zw[i]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=3;j++)
{
cin>>a;
if(j!=3)
cin.get();
for(int k=8;k>=1;k--)
{
s=a%2;
a/=2;
if(num[j*8+k]==-1)
num[j*8+k]=s;
else if(zw[j*8+k]==1&&num[j*8+k]+s==1)
{
zw[j*8+k]=0;
num[j*8+k]=0;
}
}
}
}
for(int i=1;i<=32;i++)
{
if(zw[i]==0)
{
for(i=i;i<=32;i++)
{
num[i]=0;
zw[i]=0;
}
}
}
for(int i=0;i<=3;i++)
{
a=0;
for(int k=1;k<=8;k++)
{
a*=2;
a+=num[i*8+k];
}
cout<<a;
i==3?cout<<endl:cout<<".";
}
for(int i=0;i<=3;i++)
{
a=0;
for(int k=1;k<=8;k++)
{
a*=2;
a+=zw[i*8+k];
}
cout<<a;
i==3?cout<<endl:cout<<".";
}
}
return 0;
}
Problem J. Joseph’s Problem
摘自:扬帆起航
题意:
求给出的n,k的∑1<=i<=n(k mod i).
思路:
直接暴力的话肯定要超了,所以需要想办法缩减
p = k/i
k mod i = k - p * i
k mod ( i + 1 ) = k - p * ( i + 1 ) = k mod i - p
k mod ( i + 2 ) = k - p * ( i + 2 ) = k mod i - 2 * p
对于连续的 i ,很多p都是一样的 . 相差的部分是一个等差数列 ,
i 的 范围是 从 i 到 min(k/p,n) 如果 p == 0 则 一直延续到最后
代码:
#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
int main(int argc, const char * argv[]) {
freopen("joseph.in","r", stdin);
freopen("joseph.out", "w",stdout);
long long n,k,sum1;
while (scanf("%lld%lld",&n,&k)==2) {
long long sum=0;long long s=0;
long long kk=sqrt(k);
if(n>kk){
if(n<=k){
s=k/n;
long long d=(n-k/(s+1));
sum+=((k%n)*2+(d-1)*s)*d/2;
}
for (int i=s+1; i<=kk; ++i) {
long long d=(k/i-k/(i+1));
sum+=((k%i)*2+(d-1)*i)*d/2;
}
}
if(n>k)
sum+=(n-k)*k;
for (int i=2;i<=min(k/(kk+1),n); ++i) {
sum+=k%i;
}
cout<<sum<<endl;
}
return 0;
}