http://acm.hdu.edu.cn/showproblem.php?pid=5961
给你两个有向图,问你这两个图是否都是传递的。
一个有向图是传递的,当且仅当图中任意三点a,b,c,若存在边a->b,b->c则必存在边a->c.
bfs图,若存在一个点的深度>=3,则不是。
为什么呢?道理很简单,如果bfs序列中有c点的深度是3,设父亲是b,爷爷是a,则一定存在a->b,b->c,而不存在a->c,否则c的深度就是2了
#include
using namespace std;
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
typedef long long ll;
vector<int> g1[2222],g2[2222];
bool vis1[2222],vis2[2222];
int T,n;
char c;
bool bfs(int u,vector<int> g[]) {
bool* vis;
if(g==g1)
vis=vis1;
else
vis=vis2;
queue<int> q;
int d[2222];
q.push(u); vis[u]=true; d[u]=1;
while(!q.empty()) {
int v=q.front();
q.pop();
if(d[v]==3) return false;
for(int i=0;iint next=g[v][i];
if(!vis[next]) {
d[next]=d[v]+1;
vis[next]=true;
q.push(next);
}
}
}
return true;
}
int main() {
// RE("in.txt");
// WR("out.txt");
scanf("%d",&T);
while(T--) {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
g1[i].clear();
g2[i].clear();
vis1[i]=vis2[i]=false;
}
getchar();
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
scanf("%c",&c);
if(c=='P')
g1[i].push_back(j);
else if(c=='Q')
g2[i].push_back(j);
}
getchar();
}
bool flag1=true,flag2=true;
for(int i=1;i<=n;i++) {
if(!vis1[i])
flag1=bfs(i,g1);
if(!flag1) break;
if(!vis2[i])
flag2=bfs(i,g2);
if(!flag2) break;
}
if(!flag1 || !flag2)
cout<<"N"<else
cout<<"T"<
http://acm.hdu.edu.cn/showproblem.php?pid=5963
懒得抄了
观察游戏规则,我们发现了规律,如果根节点到一个孩子的边是1,那么不管后面的边是什么样的,把这条链变成全0需要的操作次数就是奇数次,否则是偶数次,是0反之。
直观上是这么证明的:(有点像数学归纳法,我也不知道是不是)
* 如果这条链上只有1,那么需要1次是奇数。
* 假设对序列 1[01]∗1 ,操作次数是奇数,那么再添加任意多个0或任意多个1,操作次数不变,若添加序列 0+1 ,则变成全0的次数多了两次也是奇数。
对于从0开始的链亦同理。
那么得出结论:如果根节点下面的孩子节点的边中,1的个数是奇数,则需要奇数次操作变成全0,即女生胜,否则男生胜。
剩下的就是维护树边就可以了,首先以1为根,dfs/bfs确定深度和父节点,然后深度大的往深度小的父亲找,并更新权值直到相遇即可。
至于数据结构,这里用的是前向星,顺便学习了一下这种数据结构。(其实邻接表也同理,需要在深搜的时候记下每个点的父节点在邻接表中的序号,学习了不熟悉的数据结构,就懒得写了)
关于前向星,请阅读这篇文章:http://blog.csdn.net/acdreamers/article/details/16902023 ,我就不重复了。
我记得最大流算法里面的图也是用前向星做的,之前没意识到直接套模板了。。。现在好好学习一下。
关于树中边的信息维护还有一种更加高级的算法叫树链剖分,本人比较菜暂时还不懂这个。。。。
相关博文放在这,有机会看看:
树链剖分原理
树链剖分(一)(#请配合树链剖分(二)以及线段树一起食用-_-)
树链剖分(二)
树链剖分(三)(除了道馆之战——暂时可以告一段落了)
#include
using namespace std;
typedef long long ll;
int T,n,m;
struct node{
int to,next,w;
}edge[80005];
int head[40005],cnt,op;
int dep[40005],e[40005],father[40005];//dep表示第i个点的深度,father表示第i个点的父节点,e[i]表示终点为i的边的序号
void addedge(int u,int v,int w) {
edge[cnt].to=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
}
void dfs(int u,int f,int d) { //u点,父节点是f,深度是d
dep[u]=d;
father[u]=f;
for(int i=head[u];i!=-1;i=edge[i].next) {
int v=edge[i].to;
if(v!=f) {
e[v]=i;
dfs(v,u,d+1);
}
}
}
int main(){
scanf("%d",&T);
while(T--) {
scanf("%d %d",&n,&m);
memset(head,-1,sizeof(head));
cnt=0;
for(int i=1;iint u,v,w;
scanf("%d %d %d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
dfs(1,0,1);
for(int i=0;iscanf("%d",&op);
if(op) {
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
while(x!=y) {
if(dep[x]int id=e[x];
edge[id].w=edge[id^1].w=z;
x=father[x];
}
}
else {
int x;
scanf("%d",&x);
int ans=0;
for(int i=head[x];i!=-1;i=edge[i].next) {
ans+=edge[i].w;
}
if(ans&1)
printf("Girls win!\n");
else
printf("Boys win!\n");
}
}
}
return 0;
}
http://acm.hdu.edu.cn/showproblem.php?pid=5965
有一个3*n的扫雷棋盘,中间那行都已经给出了数,问有多少种埋雷方案?
枚举第一列有几个雷,则其他列有几个雷都是确定的,有0个雷或者2个雷方法数均为1,1个雷为2,连乘即可。
#include
using namespace std;
typedef long long ll;
const int maxn=11111;
const int mod=1e8+7;
char s[maxn];
int num[maxn],dp[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%s",s);
int n=strlen(s);
for(int i=0;i1]=s[i]-'0';
memset(dp,0,sizeof(dp));
ll ans=0;
for(int i=0;i<=num[1];i++)
{
dp[1]=i;
if(i>2)break;
int j;
for(j=2;j<=n;j++)
{
int k=num[j-1]-dp[j-1]-dp[j-2];
if(k>2||k<0)
break;
dp[j]=k;
}
if(j<=n)continue;
if(dp[n-1]+dp[n]!=num[n])continue;
ll res=1;
for(int i=1;i<=n;i++)
{
if(dp[i]==0||dp[i]==2)
res*=1;
else
res*=2;
res%=mod;
}
ans+=res;
ans%=mod;
}
printf("%d\n",ans);
}
return 0;
}
http://acm.hdu.edu.cn/showproblem.php?pid=5968
给你一个序列,求出每个子序列异或的结果,找到所有的结果中与xi之差的绝对值最小的一个,并输出相应子序列的长度。如果有多个答案,则输出最长的。
因为只有100个用例,每个用例的长度只有100,所以暴力搞的复杂度也不过是 O(Tmn2)≈108 ,可以过去,想优化用二分查找。
#include
using namespace std;
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
typedef long long ll;
int T,n,m;
int x[105][105];
int a[105];
void pre() {
x[0][0]=a[0];
for(int i=1;i0][i]=x[0][i-1]^a[i];
for(int i=1;ifor(int j=i;j0][j]^x[0][i-1];
}
}
}
int main() {
scanf("%d",&T);
while (T--) {
scanf("%d",&n);
for(int i=0;iscanf("%d",&a[i]);
}
pre();
scanf("%d",&m);
while(m--) {
int q;
scanf("%d",&q);
int b=123456,l=-1;
for(int i=0;ifor(int j=i;jif(abs(x[i][j]-q)abs(x[i][j]-q);
l=j-i+1;
}
else if(abs(x[i][j]-q)==b) {
if(j-i+1>l)
l=j-i+1;
}
}
}
printf("%d\n", l);
}
printf("\n");
}
}
http://acm.hdu.edu.cn/showproblem.php?pid=5969
给两个数l和r,从[l,r]中取两个数x,y,使得x|y最大,输出这个最大值。
其实这题挺符合leetcode 的画风,应该有可能出现在求职笔试面试中,引起重视!!
很明显r的二进制位大于等于l的,统计r有多少位,然后开始下手。
从高位往低位看,如果l和r对应位是相等的,则这一位是什么答案就加上什么,例:
1 0 1 0 ….
1 0 1 0 ….
很明显任意[l,r]之间的数的最高位都是这些。
那么如果遇到不同的位,例:
… 0 …
… 1 …
则x中可以取到..011111...
,y中必可以取到...100000..
,因为这两个数一定存在~~
所以只要遇到l和r出现不同数位,则答案加上...11111..
即可。
#include
using namespace std;
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
typedef long long ll;
ll l,r;
int T;
int get(ll r) {
return (r<2)?1:get(r/2)+1;
}
int main(){
scanf("%d",&T);
while(T--) {
scanf("%I64d %I64d",&l,&r);
ll ans=0;
int i=get(r)-1;
while(i>=0) {
if((r&(1LL<1LL<1LL<else
break;
i--;
}
ans+=(1LL<<(1+i))-1;
cout<
感觉我在赛场上最多只能做出3题。。。据说4题才有牌,但我感觉公司笔试题难度不输给这些题的前半部分~,是不是我也要失业了。。。【柯南思考状~】