先讲1007,有m个人,n种石头,将n种石头分给m个人,每两个人之间要么是朋友关系,要么是敌人关系,朋友的话他们必须有一种相同颜色的石头,敌人的话他们必须所有石头的颜色都不相同。另外,一个人可以不拥有任何一种石头。求m个人的所有关系是不是都能用n种石头表示出来。比赛当时找的关系是n种石头可以表示n+1个人的关系。但是一直WA,因为考虑不周。

  我们考虑这样的一种情况,我们把人分为左边和右边两部分,每边的人里面都互相为敌人,同时左边的任意一个人和右边的任意一个人都是朋友。举个例子,左边有3人,右边两人。考虑左边第一人,他和右边每一个人都要有相同的至少一种石头,那么他至少要有两种石头,而左边的每一个人的每一个石头都不能一样,那么至少要有3*2=6种石头。通过这个例子我们就可以找出规律来了,总共需要的石头量为左右人数之积。

  那么问题就转化成了,将m个人分成两组,使得乘积最大(这是m个人下的最坏的情况,如果这个情况都能满足,那么其他情况也都能满足了)。显然,将m个人均分一下得到的乘积最大,那么答案就出来了。

  代码如下:

 #include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
typedef long long ll; int main()
{
int n,m;
while(scanf("%d%d",&n,&m)==)
{
int l = n/;
int r = n-n/;
if(1LL*l*r > m) puts("F");
else puts("T");
}
}

  1006,当时比赛的时候是乱做弄出来的,只要满足下面3个条件即可:

  假设有n个队伍,

  1.每个队伍的分数都不能超过2*(n-1)。

  2.所有队伍的总分必须等于n*(n-1)。

  3.分数为奇数的队伍数必须是偶数个,每两个队伍之间都有一场平局。

  但是,题解给出的方法似乎是有一个定理的(Landau's Theorem)。

  先将分数从小到大排序一下,那么对任意前i个人的总分,必须不小于他们所能够得到的分数,即i*(i-1),同时总分必须是n*(n-1)。

  另外,如果没有平局,并且只是胜者获得1分,同样可以使用这个定理,只要改变前i个人所能获得的分数即可,即C(i,2),同时,总分也必须等于C(n,2)。

  1009,当时比赛的时候m的大小是5500,那么可以在数据规模较小的情况下暴力求补图,数据规模较大的时候找规律来做(因为数据规模较大的时候点数远远大于边数,那么一定是不止一个联通块,规律就是如果与s点相邻,距离就是2,因为可以先到另外一个联通块再到这个点来完成,如果不相邻距离就是1了,因为补图上直接可达)。现在m的数据范围变大了,得采用题解的方法,在补图上bfs。代码如下(直接看代码也是可以看懂的):

 #include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
const int N = + ; vector<int> G[N];
int n,m,s;
int dis[N]; void addEdge(int u,int v)
{
G[u].push_back(v);
} void solve()
{
set<int> sa,sb;
// 每次扩展都是向不相邻的边扩展
// sb保存的是仍未bfs过的点
queue<int> Q;
for(int i=;i<=n;i++) if(i!=s) sa.insert(i);
Q.push(s);
memset(dis,-,sizeof(dis));
dis[s] = ;
while(!Q.empty())
{
int x = Q.front();Q.pop();
for(int i=;i<G[x].size();i++)
{
int v = G[x][i];
if(!sa.count(v)) continue;
sa.erase(v);
sb.insert(v);
}
for(set<int>::iterator it = sa.begin();it!=sa.end();it++)
{
dis[*it] = dis[x] + ;
Q.push(*it);
}
sa.swap(sb);
sb.clear();
}
int fir = ;
for(int i=;i<=n;i++)
{
if(i!=s)
{
if(fir) printf(" ");
else fir = ;
printf("%d",dis[i]);
}
}
puts("");
} int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++) G[i].clear(); for(int i=;i<=m;i++)
{
int u,v;scanf("%d%d",&u,&v);
addEdge(u,v);
addEdge(v,u);
} scanf("%d",&s);
solve();
}
}

  1008,题意是给一个区间L和R,求a[L]%a[L+1]%a[L+2]...%a[R]。

  一个性质:a%b,如果a小于b,那么a不变,否则,得到的数小于等于a/2(这个性质不知道要怎么证,不过似乎就是这样的样子- -)。

  那么每次取余以后都会减半,那么复杂度就是log级别的了。

  那么,我们每次找出[L+1,R]中第一个不比最左边的数大的数的位置,并不断的缩小区间即可。关于如何找,这里用了线段树来维护一个区间内的最小值,那么我们就可以用线段树找出我们需要的东西了~具体见代码:

 #include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
#include <set>
#define t_mid (l+r >> 1)
#define ls (o<<1)
#define rs (o<<1 | 1)
#define lson ls,l,t_mid
#define rson rs,t_mid+1,r
using namespace std;
typedef long long ll;
const int N = + ; int a[N],c[N<<],n;
void build(int o,int l,int r)
{
if(l==r) {c[o] = a[l];return;}
build(lson);
build(rson);
c[o] = min(c[ls],c[rs]);
} int query(int ql,int qr,int o,int l,int r,int x)
{
if(l==r)
{
if(c[o] <= x) return l;
else return -;
}
int ans = -;
if(t_mid >= ql && c[ls] <= x)
{
ans = query(ql,qr,lson,x);
if(ans == -)
{
if(t_mid < qr && c[rs] <= x) ans = query(ql,qr,rson,x);
}
return ans;
}
else if(t_mid < qr && c[rs] <= x) return query(ql,qr,rson,x);
return -;
} int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=;i<=n;i++) scanf("%d",a+i);
build(,,n); int q;scanf("%d",&q);
while(q--)
{
int ql,qr;scanf("%d%d",&ql,&qr);
int now = a[ql];
while(ql < qr)
{
ql = query(ql+,qr,,,n,now);
if(ql == -) break;
now %= a[ql];
}
printf("%d\n",now);
}
}
}

线段树维护

当然,也可以使用单调栈来维护,代码如下(只有主程序部分的代码):

 int a[N],nxt[N];
stack<int> s;
int main(){
int T,n,m,l,r;
cin >> T;
while(T --){
memset(nxt,-,sizeof(nxt));
while(!s.empty()) s.pop();
cin >> n;
for(int i = ; i <= n ; i ++) scanf("%d",&a[i]);
cin >> m;
for(int i = ; i <= n ; i ++){
// 单调递增的栈。
if(s.empty() || a[i] >= a[s.top()]) s.push(i);
else{
while(!s.empty() && a[i] < a[s.top()]){
nxt[s.top()] = i;
s.pop();
}
s.push(i);
}
}
while(m --){
scanf("%d%d",&l,&r);
int ret = a[l] , tmp = l;
while(nxt[tmp] <= r && nxt[tmp] != -){
tmp = nxt[tmp];
ret = ret % a[tmp];
}
cout << ret << endl;
}
}
return ;
}

单调栈维护

  1010,直接dfs,我们在dfs的过程中,记录下各个数字出现的情况,然后遇到一个新的数字a[i]的时候,我们查找小于等于k/a[i]的数字有多少个就可以了(这就是对答案的贡献)。那么我们可以用树状数组或者线段树维护各个数字出现的情况以及区间和。另外这个dfs的过程遇到分支时,先让该位置的数字出现次数加1,回溯的时候再减掉就可以了。代码如下:

 #include <stdio.h>
#include <algorithm>
#include <string.h>
#include <vector>
using namespace std;
const int N = (int)1e5+;
typedef long long ll; int n,m;
ll k,ans;
ll a[N*],b[N];
vector<int> G[N];
bool vis[N];
int c[N*]; int getsum(int x)
{
int ret = ;
while(x)
{
ret += c[x];
x -= (x&-x);
}
return ret;
} void update(int x,int dt)
{
while(x <= m)
{
c[x] += dt;
x += (x&-x);
}
} void dfs(int x)
{
int can = lower_bound(a+,a++m,k/b[x]) - a;
// 之所以在main里面需要把k/a[i]都当作一个元素,是因为要避免在这里查找的时候出问题
// 比方说现在的b[x]是5,那么之前储存的数字中小于等于20的都是可以成立的,但是现在假设没有储存20这个数字,
// 那么lower_bound找到的是21,而如果21恰好有的话,那就出错了!(因为我们要的是<=20的数字的个数)
int pos = lower_bound(a+,a++m,b[x]) - a;
ans += getsum(can);
update(pos,);
for(int i=;i<G[x].size();i++) dfs(G[x][i]);
update(pos,-);
} int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%d%I64d",&n,&k);
for(int i=;i<=n;i++) G[i].clear();
memset(vis,false,sizeof(vis));
memset(c,,sizeof(c));
ans = ; for(int i=;i<=n;i++) {scanf("%I64d",a+i);b[i]=a[i];}
for(int i=;i<n;i++)
{
int u,v;scanf("%d%d",&u,&v);
G[u].push_back(v);
vis[v] = true;
} for(int i=;i<=n;i++) a[i+n] = k/a[i];
sort(a+,a++n*);
m = unique(a+,a++n*) - (a+);
for(int i=;i<=n;i++)
{
if(!vis[i])
{
dfs(i);
break;
}
}
printf("%I64d\n",ans);
}
}

2016 ICPC 大连网络赛 部分题解的更多相关文章

  1. HDU 5869.Different GCD Subarray Query-区间gcd+树状数组 (神奇的标记右移操作) (2016年ICPC大连网络赛)

    树状数组... Different GCD Subarray Query Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/6 ...

  2. HDU 5875 Function (2016年大连网络赛 H 线段树+gcd)

    很简单的一个题的,结果后台数据有误,自己又太傻卡了3个小时... 题意:给你一串数a再给你一些区间(lef,rig),求出a[lef]%a[lef+1]...%a[rig] 题解:我们可以发现数字a对 ...

  3. HDU 5867 Sparse Graph (2016年大连网络赛 I bfs+补图)

    题意:给你n个点m条边形成一个无向图,问你求出给定点在此图的补图上到每个点距离的最小值,每条边距离为1 补图:完全图减去原图 完全图:每两个点都相连的图 其实就是一个有技巧的bfs,我们可以看到虽然点 ...

  4. HDU 5877 Weak Pair (2016年大连网络赛 J dfs+反向思维)

    正难则反的思想还是不能灵活应用啊 题意:给你n个点,每个点有一个权值,接着是n-1有向条边形成一颗有根树,问你有多少对点的权值乘积小于等于给定的值k,其中这对点必须是孩子节点与祖先的关系 我们反向思考 ...

  5. 2016 ACM/ICPC Asia Regional Dalian ICPC大连现场赛

    讲道理我挺想去大连的…… 毕竟风景不错…… 而且这次能去北京也是靠大连网络赛这一场拉开的优势…… 一道补图最短路一道数学推论简直爽爆…… 当然 除了这一场 其他场都非常划水…… 上次看到别人的博客用这 ...

  6. 大连网络赛 1006 Football Games

    //大连网络赛 1006 // 吐槽:数据比较水.下面代码可以AC // 但是正解好像是:排序后,前i项的和大于等于i*(i-1) #include <bits/stdc++.h> usi ...

  7. 2018 ICPC 沈阳网络赛

    2018 ICPC 沈阳网络赛 Call of Accepted 题目描述:求一个算式的最大值与最小值. solution 按普通算式计算方法做,只不过要同时记住最大值和最小值而已. Convex H ...

  8. 2018 ICPC 徐州网络赛

    2018 ICPC 徐州网络赛 A. Hard to prepare 题目描述:\(n\)个数围成一个环,每个数是\(0\)~\(2^k-1\),相邻两个数的同或值不为零,问方案数. solution ...

  9. 2019 ICPC 南昌网络赛

    2019 ICPC 南昌网络赛 比赛时间:2019.9.8 比赛链接:The 2019 Asia Nanchang First Round Online Programming Contest 总结 ...

随机推荐

  1. java——包装类中的equals方法

    基本数据类型包装类中的equals方法用于比对相同包装类中的值是否相等,如果两者比较的包装类类型不同则返回false: Byte public boolean equals(Object obj) { ...

  2. JavaScript例子2-使一个特定的表格隔行变色

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  3. Qtspim和MIPS的坑

    Qtspim和MIPS的坑 数组要么用空格隔开,要么逗号之后再加一个空格 乘法的结果保存在(HI,LO)寄存器中,但是不能直接通过Move得到,必须使用mfhi 和mflo指令 用户输入的数组最后一个 ...

  4. kali入侵服务器的那一套实战

    dnsenum  -enum       xxxxx.com 枚举出网站的所有域名和服务器的ip地址 打开百度查询ip地址的所在地 whatweb xxxx.com 查看那些网站入口可以访问   以状 ...

  5. CSS选择器(通配符选择器、标签选择器、类选择器、id选择器、群组选择器、后代选择器、子元素选择器和相邻元素选择器)

    通配符选择器  *   与任何元素匹配 派生选择器: 后代选择器(包含选择器):后代选择器可以选择作为元素后代的元素 A B    对A元素中的B元素应用样式 后代选择器中两个元素间的层次间隔可以是无 ...

  6. #!/usr/bin/node 是什么意思

    // 调用系统环境变量中的解释器执行文件 #!/usr/bin/node //如果不是默认安装位置这个地方可能就找不到,那么文件就是报错,所以有了另一种写法 #!/usr/bin/env node

  7. 2 java开发环境的配置步骤

    1  首先需要下载JDK(以java se development kit java标准版开发包) 8.0 如果只是单纯的运行java程序则只需要安装JRE(java runtime envirome ...

  8. axios表单提交,delete,get请求(待完善)

    import { mapMutations} from 'vuex' import axios from 'axios' const mixins = { data() { return { } }, ...

  9. java_day02_标识符等

    ch02 目标: 1. 标识符.关键字和类型介绍 2. 如何构建类---------------------------------1.注释 作用:使部分内容只为程序员可见,不为编译器所编译.虚拟机所 ...

  10. Linux CentOS 7 防火墙与端口设置操作

    CentOS升级到7之后用firewall代替了iptables来设置Linux端口, 下面是具体的设置方法: []:选填 <>:必填 [<zone>]:作用域(block.d ...