由于前天熬夜,昨天开赛前两个小时没点动静,吐槽下题目,真长!
Problem A. Triangles
给你N个点M条边(N<200,M<20000),点的下标为-100000~100000,现在问这些个点能组成多少个三角形。
首先,若点A,B,C共线,AB,BC有边,那么AC也为有边,我们要把这些情况都预处理掉。因为最多200个点,这里可以用到floyd思想,枚举中间点K,看I,J,K是否满足情况,满足连上IJ边。接下来就好做了,直接枚举三个点看是否能构成三角形~~
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<map>
#include<string.h>
#include<math.h>
using namespace std;
struct node
{
double x,y;
}p[210];
map<double,int> mymap;
double tomap[210];
int ans,n,m,mp[210][210];
double hash(double x,double y)
{
x*=1000000;
return x+y;
}
int online(node a,node b,node c)
{
double x,y,z,t;
x=a.x-c.x;
y=a.y-c.y;
z=b.x-c.x;
t=b.y-c.y;
if((t<1e-8&&t>-1e-8)&&(y<1e-8&&y>-1e-8))return 1;
else
{
if((t<1e-8&&t>-1e-8)||(y<1e-8&&y>-1e-8))return 0;
t=(x/y)-(z/t);
return (t<1e-8&&t>-1e-8);
}
}
double dis(node a,node b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int rejudge(node a,node b,node c)
{
double x,y,z;
x=dis(a,b);
y=dis(a,c);
z=dis(b,c);
if(x+y-z>1e-8&&x+z-y>1e-8&&y+z-x>1e-8)
return 1;
else return 0;
}
int istrue(int a,int b,int c)
{
if (mp[a][b]&&mp[a][c]&&mp[b][c]) return rejudge(p[a],p[b],p[c]);
return 0;
}
int main()
{
int i,j,k;
while (scanf("%d%d",&n,&m)!=EOF)
{
mymap.clear();
memset(mp,0,sizeof(mp));
double tx1,ty2,tx2,ty1;
for (i=0;i<n;i++)
{
scanf("%lf%lf",&tx1,&ty1);
p[i].x=tx1,p[i].y=ty1;
tomap[i]=hash(tx1,ty1);
mymap[tomap[i]]=i;
}
for (i=0;i<m;i++)
{
scanf("%lf%lf%lf%lf",&tx1,&ty1,&tx2,&ty2);
mp[mymap[hash(tx1,ty1)]][mymap[hash(tx2,ty2)]]=1;
mp[mymap[hash(tx2,ty2)]][mymap[hash(tx1,ty1)]]=1;
}
for (i=0;i<n;i++)
for (j=0;j<n;j++)
{
if (i==j) continue;
for (k=0;k<n;k++)
{
if (k==i||k==j) continue;
if (!mp[j][k]&&mp[i][j]&&mp[i][k]&&online(p[i],p[j],p[k]))
mp[j][k]=1,mp[k][j]=1;
}
}
ans=0;
for (i=0;i<n;i++)
for (j=i+1;j<n;j++)
for (k=j+1;k<n;k++)
ans+=istrue(i,j,k);
printf("%d\n",ans);
}
return 0;
}
Problem B. Dating With Girls
给定一个有向图,图中节点各有一个权值w[ i ],求从0节点出发,到权值最小的点(权值有负值)的路径中,能获得的总权值最大的那个值,路径上不能有负权点(终点必须为负)。
很简单可以想到,BFS求路径,然后~~就是这样做了。
Problem C. Anti-Virus
有N个病毒,给定一条字符串,问字符串中含有多少种病毒。
AC自动机模版题。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct node
{
int next[26],id,flag,fail;
}tree[150*2010];
int last,used[2010];
char s[100010];
int queue[150*2010];
void insert(char *str,int len,int id)
{
int i,now=1;
for (i=0;i<len;i++)
if (tree[now].next[str[i]-'a'])
now=tree[now].next[str[i]-'a'];
else
{
tree[now].next[str[i]-'a']=last;
now=last++;
memset(&tree[now],0,sizeof(node));
}
tree[now].id=id;
}
void buildtrie()
{
int l,r,now,i;
l=r=0;
queue[r++]=1;
while (l<r)
{
now=queue[l++];
for (i=0;i<26;i++)
if (tree[now].next[i])
{
queue[r++]=tree[now].next[i];
tree[tree[now].next[i]].fail=tree[tree[now].fail].next[i];
}else
tree[now].next[i]=tree[tree[now].fail].next[i];
}
}
void mask(int place)
{
while (place)
{
if (!place || tree[place].flag) break;
used[tree[place].id]=1;
tree[place].flag=1;
place=tree[place].fail;
}
}
int solved(int n)
{
int cnt=0,i;
for (i=1;i<=n;i++)
if (used[i]==1) cnt++;
return cnt;
}
int main()
{
int T,i,j,k,n,len,place,ans;
char str[200];
scanf("%d",&T);
while (T--)
{
memset(&tree[0],0,sizeof(node));
memset(&tree[1],0,sizeof(node));
memset(used,0,sizeof(used));
for (i=0;i<26;i++)
tree[0].next[i]=1;
last=2;
scanf("%d",&n);
for (i=1;i<=n;i++)
{
scanf("%s",str);
insert(str,strlen(str),i);
}
buildtrie();
scanf("%s",s);
len=strlen(s);
place=1,i=0;
while (i<len)
{
if (s[i]>='a'&&s[i]<='z')
{
place=tree[place].next[s[i]-'a'];
mask(place);
i++;
}else
{
int tmp=0;
for (++i;s[i]>='0'&&s[i]<='9';i++)
tmp=tmp*10+s[i]-'0';
char tc=s[i++];
cout<<tc<<" ";
for (j=0;j<tmp;j++)
{
if (place==tree[place].next[tc-'a']) break;
place=tree[place].next[tc-'a'];
}
i++;
}
}
ans=solved(n);
if (ans) printf("Yes\n%d\n",ans);
else printf("No\n0\n");
}
return 0;
}
Problem E. The mell hall
给定A[ I ] B[ I ],M[ I ]=(A1A2...Ai-1)/Bi。AI,BI为关联关系,AIBI位置可任意,求使得M[ I ]最大的那个元素在元数列中的位置。
因为(Bi < 10 < Ai*Bi),那么对于任意的M[ I ]/M[ I+1]有其小于1,也就是说M[ I ]<M[ I+1]。也就是说只要把元素I放到队列最后,就可以求出这个元素可以得到的最大M[ ],算出所有的M[ ],找到最大的那个的原坐标~~
Problem F. A new tree game
给你一棵树,N个点,M条边,每条边可以砍K下砍断,砍断的边及其所链接的子树将消失。两个人来砍边,不能砍了就输了,问先手还是后手赢。
博弈,SG函数。
题解链接:http://www.cnblogs.com/jianglangcaijin/archive/2012/12/18/2824053.html
Problem H. Little Sheep and a paper
折纸,可以向上向下向左向右折,问经过某些操作之后,面向自己这面的纸上有多少条突出的折痕。
显然第一下折是不会产生突出折痕的。若不记第一下,那么
剩下的操作中,竖着折N下的突出折痕为2^n-1, 若横着再折M下就要翻M翻 也就是2^m 那么S1=(2^n-1)2^m。
同理可得S2= ((2^m-1)2^n),ans=S1+S2。
Problem I. Matrix
给你一个N*N的01矩阵A,问A乘以A的转置阵B后,矩阵中有多少个元素不为0。
显然,这就是将矩阵的行乘以自己的行,也就是说,枚举每一行为得到矩阵的元素的行数,再枚举每一行为得到矩阵元素的列数,看那两行中不为0的元素中是否有相同的列下标,若有则表明得到矩阵的该元素不为0。因为只有原矩阵元素不为0的元素相乘才会生成不为0的元素,所以只要扫不为0的点,最坏情况也就是1000*1000的复杂度。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<map>
using namespace std;
map<int,int> mymap;
struct node
{
int x,y;
}p[1010];
int cmp(node a,node b)//按行排序
{
return a.x<b.x;
}
int main()
{
int t,n,m,i,j,k,ans;
int lb[1010];
scanf("%d",&t);
while (t--)
{
memset(lb,0,sizeof(lb));
scanf("%d%d",&n,&m);
for (i=0;i<m;i++)
scanf("%d%d",&p[i].x,&p[i].y);
sort(p,p+m,cmp);
j=0;
for (i=1;i<m;i++){
if (p[i].x!=p[i-1].x) lb[++j]=i;//lb数组维护每一行从哪个下标开始
}
ans=0;
for (i=0;i<=j;i++)
{
mymap.clear();
for (k=lb[i];k<(i==j?m:lb[i+1]);k++){
mymap[p[k].y]=1;
}
int row=0;
k=0;
for (k=0;k<=j;k++)
{
for (row=lb[k];row<(k==j?m:lb[k+1]);row++)
if (mymap.find(p[row].y)!=mymap.end())
{
ans++;
break;
}
}
}
printf("%d\n",ans);
}
return 0;
}