1、
危险道路
题目描述
天凯是苏联的总书记。苏联有n个城市,某些城市之间修筑了公路。任意两个城市都可以通过公路直接或者间接到达。 天凯发现有些公路被毁坏之后会造成某两个城市之间无法互相通过公路到达。这样的公路就被称为dangerous pavement。 为了防止美帝国对dangerous pavement进行轰炸,造成某些城市的地面运输中断,天凯决定在所有的dangerous pavement驻扎重兵。可是到底哪些是dangerous pavement呢?你的任务就是找出所有这样的公路。
输入格式
第一行n,m(1<=n<=100000, 1<=m<=300000),分别表示有n个城市,总共m条公路。
以下m行每行两个整数a, b,表示城市a和城市b之间修筑了直接的公路。
输出格式
输出有若干行。每行包含两个数字a,b(a < b),表示 < a,b >是dangerous pavement。请注意:输出时,所有的数对< a,b>必须按照a从小到大排序输出;如果a相同,则根据b从小到大排序。
样例数据
input
6 6
1 2
2 3
2 4
3 5
4 5
5 6
output
1 2
5 6
数据规模与约定
时间限制:1s1s
空间限制:256MB
tarjan割边。
#include<bits/stdc++.h>
using namespace std;
int n,m,linkk[21000],t,root,dfn[21000],low[21000],ind,tot;
bool flag[51000];
struct node{
int n,y,id;
}e[101000];
struct init{
int a,b;
}ini[101000];
struct anss{
int a,b;
}ans[101000];
int read() {
bool flag=true;
int num=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false;
for(;c>='0'&&c<='9';c=getchar())
num=(num<<3)+(num<<1)+c-48;
if(flag) return num;
else return -num;
}
void insert(int aa,int bb,int cc){
e[++t].n=linkk[aa];
e[t].y=bb;
e[t].id=cc;
linkk[aa]=t;
e[++t].n=linkk[bb];
e[t].y=aa;
e[t].id=cc;
linkk[bb]=t;
return;
}
void tarjan(int x,int fa){
dfn[x]=low[x]=++ind;
for(int i=linkk[x];i;i=e[i].n){
int v=e[i].y;
if(e[i].id==fa) continue;
if(!dfn[v]){
tarjan(v,e[i].id);
if(low[v]<low[x]) low[x]=low[v];
if( low[v]>dfn[x] )
flag[e[i].id]=true;
}
else
if(dfn[v]<low[x])
low[x]=dfn[v];
}
return;
}
void init(){
n=read();m=read();
int aa,bb;
for(int i=1;i<=m;++i){
aa=read();
bb=read();
ini[i].a=aa;
ini[i].b=bb;
insert(aa,bb,i);
}
for(int i = 1;i <= n;++i)
if(!dfn[i]){
root=i;
tarjan(i,0);
}
return;
}
bool mycup(anss x,anss y){
return x.a<y.a||(x.a==y.a&&x.b<y.b);
}
int main() {
init();
for(int i=1;i<=m;++i)
if(flag[i])
ans[++tot].a=min(ini[i].a,ini[i].b),
ans[tot].b=max(ini[i].a,ini[i].b);
sort(ans+1,ans+tot+1,mycup);
for(int i=1;i<=tot;++i)
printf("%d %d\n",ans[i].a,ans[i].b);
return 0;
}
2、
轮船问题
某国家被一条河划分为南北两部分,在南岸和北岸总共有N对城市,每一城市在对岸都有一个城市作为友好城市。每一对友好城市都希望有一条航线来往,于是他们向政府提出了申请。
由于河终年有雾。政府决定允许开通的航线就互不交叉(如果两条航线交叉,将有很大机会撞船)。兴建哪些航线以使在安全条件下有最多航线可以被开通。
输入格式
第一行两个由空格分隔的整数x,y,10〈=x,y〈=60000,x,y中较长的表示河的长度另一个表示宽。
第二行是一个整数N(1<=N<=5000),表示分布在河两岸的城市对数。接下来的N行每行有两个由空格分隔的正数C,D(C、D〈=x〉,描述每一对友好城市与河起点的距离,C表示北岸城市的距离而D表示南岸城市的距离。在河的左边,任何两个城市的位置都是不同的。
输出格式
一个整数,表示安全条件下能够开通的最大航线数目
样例数据
input
30 4
5
4 5
2 4
5 2
1 3
3 1
output
3
数据规模与约定
ceoi经典问题
时间限制:1s1s
空间限制:256MB
很裸的dp,按照左端点排序,然后枚举之前的点对,满足要求的点对求最大值即可
#include<bits/stdc++.h>
using namespace std;
int f[5010],n;
struct node{
int l,r;
}a[5010];
int read() {
bool flag=true;
int num=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false;
for(;c>='0'&&c<='9';c=getchar())
num=(num<<3)+(num<<1)+c-48;
if(flag) return num;
else return -num;
}
bool mycup(node x,node y){
return x.l<y.l;
}
void init(){
int x=read(),y=read();
n=read();
for(int i=1;i<=n;++i){
a[i].l=read();
a[i].r=read();
}
sort(a+1,a+n+1,mycup);
return;
}
void solve(){
f[1]=1;
for(int i=2;i<=n;++i)
{
f[i]=1;
for(int j=1;j<i;++j)
if(a[j].r<a[i].r)
f[i]=max(f[i],f[j]+1);
}
int ans=0;
for(int i=1;i<=n;++i) ans=max(ans,f[i]);
printf("%d",ans);
return;
}
int main(){
init();
solve();
return 0;
}
3、
【noip2010】乌龟棋
小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。
乌龟棋的棋盘是一行N 个格子,每个格子上一个分数(非负整数)。棋盘第1格是唯一的起点,第N 格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。
乌龟棋中M 张爬行卡片,分成4 种不同的类型(M 张卡片中不一定包含所有4 种类型的卡片,见样例),每种类型的卡片上分别标有1、2、3、4 四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。
游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。
很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。
现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?
输入格式
输入文件名tortoise.in。输入文件的每行中两个数之间用一个空格隔开。
第1 行2 个正整数N 和M,分别表示棋盘格子数和爬行卡片数。
第2 行N 个非负整数,a1, a2, ……, aN,其中ai 表示棋盘第i 个格子上的分数。
第3 行M 个整数,b1,b2, ……, bM,表示M 张爬行卡片上的数字。
输入数据保证到达终点时刚好用光M 张爬行卡片,即
N−1=∑m1biN−1=∑1mbi
输出格式
输出只有1 行,1 个整数,表示小明最多能得到的分数。
样例数据
input
9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1
output
73
{小明使用爬行卡片顺序为1,1,3,1,2,得到的分数为6+10+14+8+18+17=73。注意, 由于起点是1,所以自动获得第1 格的分数6。}
数据规模与约定
对于30%的数据有1 ≤ N≤ 30,1 ≤M≤ 12。 对于50%的数据有1 ≤ N≤ 120,1 ≤M≤ 50,且4 种爬行卡片,每种卡片的张数不会超 过20。 对于100%的数据有1 ≤ N≤ 350,1 ≤M≤ 120,且4 种爬行卡片,每种卡片的张数不会 超过40;0 ≤ ai ≤ 100,1 ≤ i ≤ N;1 ≤ bi ≤ 4,1 ≤ i ≤M。
时间限制:1s1s
空间限制:256MB256MB
很暴力的dp,开四维表示四种牌使用的个数(开三维也一样)
#include<bits/stdc++.h>
using namespace std;
int f[41][41][41][41],a[360],n,m;
int num[5];
int read() {
bool flag=true;
int num=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false;
for(;c>='0'&&c<='9';c=getchar())
num=(num<<3)+(num<<1)+c-48;
if(flag) return num;
else return -num;
}
void init(){
n=read();m=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=m;++i) num[read()]++;
return;
}
void solve(){
f[0][0][0][0]=a[1];
for(int n1=0;n1<=num[1];n1++)
for(int n2=0;n2<=num[2];n2++)
for(int n3=0;n3<=num[3];n3++)
for(int n4=0;n4<=num[4];n4++)
{
int x=n1*1+n2*2+n3*3+n4*4+1;
if(n1!=0) f[n1][n2][n3][n4]=max( f[n1][n2][n3][n4] , f[n1-1][n2][n3][n4]+a[x] );
if(n2!=0) f[n1][n2][n3][n4]=max( f[n1][n2][n3][n4] , f[n1][n2-1][n3][n4]+a[x] );
if(n3!=0) f[n1][n2][n3][n4]=max( f[n1][n2][n3][n4] , f[n1][n2][n3-1][n4]+a[x] );
if(n4!=0) f[n1][n2][n3][n4]=max( f[n1][n2][n3][n4] , f[n1][n2][n3][n4-1]+a[x] );
}
printf("%d",f[num[1]][num[2]][num[3]][num[4]]);
return;
}
int main(){
init();
solve();
return 0;
}
4、
ac自动机模板
给定n个单词,一篇文章,问有多少个单词在这篇文章中出现过
输入格式
第一行:一个整数n(n<=10000)
接下来n行,每行一个单词。保证单词长度<=50
最后一行:一个长度为m的字符串,表示这篇文章。m<=1000000
输出格式
一个整数,表示多少个单词出现过。
input
5
she
he
say
shr
her
yasherhs
output
3
数据规模与约定
hdoj 2222
时间限制:1s
空间限制:256MB
#include<bits/stdc++.h>
using namespace std;
struct tri_tree{int linkk[26],fail,endflag;}tr[600000];
int head,tail,q[600000],t,ans;
char s[1001000];
int read() {
bool flag=true;
int num=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=false;
for(;c>='0'&&c<='9';c=getchar())
num=(num<<3)+(num<<1)+c-48;
if(flag) return num;
else return -num;
}
void add(){
int node=0;
for(int i = 0;i < strlen(s) ;++i){
int c = s[i] - 'a';
if( !tr[node].linkk[c] )
tr[node].linkk[c] = ++t;
node = tr[node].linkk[c];
}
tr[node].endflag++;
return;
}
void init(){
int n = read();
for(int i = 1;i <= n;++i) {
scanf("%s",s);
add();
}
return;
}
void AC(){
head = 0;tail = 0;q[tail]=0;
while(head <= tail){
int now = q[head++];
for(int i = 0;i < 26;++i){
int node = tr[now].linkk[i] , Fail = tr[now].fail;
if(node>0){
if(!now) tr[node].fail = 0;
else tr[node].fail = tr[Fail].linkk[i];
q[++tail] = node;
}
else
if(!now)
tr[now].linkk[i] = 0;
else
tr[now].linkk[i] = tr[Fail].linkk[i];
}
}
return;
}
void find(){
ans=0;
int now=0;
scanf("%s",s);
int len=strlen(s);
for(int i=0;i<len;++i)
{
int chindex=s[i]-'a';
while(!tr[now].linkk[chindex]&&now!=0) now=tr[now].fail;
now=tr[now].linkk[chindex];
int temp=now;
while(temp!=0&&tr[temp].endflag>-1)
{
ans+=tr[temp].endflag;
tr[temp].endflag=-1;
temp=tr[temp].fail;
}
}
printf("%d",ans);
return;
}
int main(){
init();
AC();
find();
return 0;
}