10.23模拟赛

U14259 叉叉

题目描述

现在有一个字符串,每个字母出现的次数均为偶数。
接下来我们把第一次出现的字母a和第二次出现的a
连一条线,第三次出现的和四次出现的字母a连一条线,
第五次出现的和六次出现的字母a连一条线..
.对其他25个字母也做同样的操作。
现在我们想知道有多少对连线交叉。
交叉的定义为一个连线的端点在另外一个连线的内部,
另外一个端点在外部。
下图是一个例子,共有三对连线交叉(我们连线的时候,只能从字符串上方经过)。

输入输出格式

输入格式:

一行一个字符串。保证字符串均由小写字母组成,且每个字母出现次数为偶数次。

输出格式:

一个整数,表示答案。

输入输出样例

输入样例#1:

abaazooabz

输出样例#1:

3

说明

对于30% 的数据,字符串长度不超过50。
对于100% 的数据,字符串长度不超过100,000。

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;

struct list{
    int pos,next,ji;
}l[10000005];
int head[27];
int num;
int f[27];
int ans;
int a[27];
int b[27];

void add(int sum,int pos){
    l[++num].pos=pos;
    l[num].next=head[sum];
    head[sum]=num;
    if(!f[sum]){f[sum]=1;l[num].ji=true;}
    else {f[sum]=0;l[num].ji=false;}
}

string str;    
    
int main(){
//    freopen("a.in","r",stdin);
//    freopen("b.out","w",stdout);
    cin>>str;
    for(int i=0;i<str.size();++i){
        add(str[i]-'a',i);
    }
    for(int i=str.size()-1;i>-1;--i){
        int sum=str[i]-'a';
        if(head[sum]!=i+1)continue;
        a[sum]=head[sum];
        b[sum]=l[head[sum]].next;
        for(int j=a[sum]-2;j>b[sum]-1;--j){
            int sumbre=str[j]-'a';
            if(j>head[sumbre]-1)continue;
            if(l[j+1].ji)continue;
            a[sumbre]=j+1;
            b[sumbre]=l[j+1].next;
            if(b[sumbre]<b[sum])ans++;
        }
        head[sum]=l[l[head[sum]].next].next;
    }
    cout<<ans;
    return 0;
}

做法

链表
感觉我这是一个O(n2)的程序,但是肯定不是O(n2)因为跑过了而且还很快
Lqz大佬和我都是用链表做的,他说是严格O(n)的,我的程序和它不太一样
我真的感觉这是一个\(O(n^2)\)
但是至少\(10^6\)能完成
正解前缀和……
还有写树状数组和线段树的反正做法很多

U14261 跳跳虎回家

题目描述

跳跳虎在外⾯出去玩忘了时间,现在他需要在最短的时间内赶回家。
跳跳虎所在的世界可以抽象成⼀个含有 个点的图(点编号从 到 ),
跳跳虎现在在 号点,跳跳虎的家在 号点。
图上⼀共有 条单向边,通过每条边有固定的时间花费。
同时,还存在若⼲个单向传送通道,传送通道也有其时间花费。
传送通道⼀般来说⽐普通的道路更快,但是跳跳虎最多只能使⽤ 次。
跳跳虎想知道他回到家的最⼩时间消耗是多少。

输入输出格式

输入格式:

第一行4个整数\(n,m,q,k\)
( n表示点数,m表示普通道路的数量, q表示传送通道的数量,
k表示跳跳虎最多使k次传送通道)
接下来 n行每3 个整数a,b,c ,表示有1条从a到b,时间花费为c的普通道路
接下来 q每3个整数a,b,c,表示有1条从a到b,时间花费为c的传送道路
输出格式:
输出一行1个整数表示最少时间消耗,如果没法回到家输出-1。

输入输出样例

输入样例#1:

5 5 2 1
1 2 1
1 3 2
2 4 2
3 4 3
4 5 4
1 4 1
2 5 1

输出样例#1:

2

说明

对于30%的数据, 1 ≤ n ≤ 500,0 ≤ m,q ≤ 2000,k = 0
对于另外30%的数据, 1 ≤ n ≤ 500,0 ≤ m,q ≤ 2000,k = 1
对于100%的数据 1 ≤ n ≤ 500,0 ≤ m,q ≤ 2000,0 ≤ k ≤ \(10^9\)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define N 200005
using namespace std;

int a,b,c;
int n,m,q,k;

struct edge{
    int v,next,cost;
}E[N],e[N];
int h1[N],h2[N];
int t1,t2;

struct node{
    int k,c;
    bool operator < (const node & s)const{
        return c<s.c;return k<s.k;
    }
};

void add(int a,int b,int c){
    E[++t1].v=b;
    E[t1].cost=c;
    E[t1].next=h1[a];
    h1[a]=t1;
}

void push(int a,int b,int c){
    e[++t2].v=b;
    e[t2].cost=c;
    e[t2].next=h2[a];
    h2[a]=t2;
}

queue<int>que;
bool vis[N];
int dis[N][2];
//priority_queue<node>Q[N];

void SPFA(){
    que.push(1);
    vis[1]=1;
    memset(dis,0x3f,sizeof(dis));
    dis[1][0]=0;
    while(!que.empty()){
        int sss=que.front();
        que.pop();
        vis[sss]=0;
        for(int i=h1[sss];i;i=E[i].next){
            int v=E[i].v;
            if(dis[v][0]>dis[sss][0]+E[i].cost){
                dis[v][0]=dis[sss][0]+E[i].cost;
                if(!vis[v]){
                    que.push(v);
                    vis[v]=1;
                }
            }
            if(dis[v][1]>dis[sss][1]+E[i].cost){
                dis[v][1]=dis[sss][1]+E[i].cost;
                if(!vis[v]){
                    que.push(v);
                    vis[v]=1;
                }
            }
        }
        for(int i=h2[sss];i;i=e[i].next){
            int v=e[i].v;
            if(dis[v][1]>dis[sss][0]+e[i].cost){
                dis[v][1]=dis[sss][0]+e[i].cost;
                if(!vis[v]){
                    que.push(v);
                    vis[v]=1;
                }
            }
        }
    }
}

void spfa(){
    que.push(1);
    vis[1]=1;
    memset(dis,0x3f,sizeof(dis));
    dis[1][0]=0;
    while(!que.empty()){
        int sss=que.front();
        que.pop();
        vis[sss]=0;
        for(int i=h1[sss];i;i=E[i].next){
            int v=E[i].v;
            if(dis[v][0]>dis[sss][0]+E[i].cost){
                dis[v][0]=dis[sss][0]+E[i].cost;
                if(!vis[v]){
                    que.push(v);
                    vis[v]=1;
                }
            }
        }
    }
}

void Spfa(){
    que.push(1);
    vis[1]=1;
    memset(dis,0x3f,sizeof(dis));
    dis[1][0]=0;
    while(!que.empty()){
        int sss=que.front();
        que.pop();
        vis[sss]=0;
        for(int i=h1[sss];i;i=E[i].next){
            int v=E[i].v;
            if(dis[v][0]>dis[sss][0]+E[i].cost){
                dis[v][0]=dis[sss][0]+E[i].cost;
                if(!vis[v]){
                    que.push(v);
                    vis[v]=1;
                }
            }
        }
        for(int i=h2[sss];i;i=e[i].next){
            int v=e[i].v;
            if(dis[v][0]>dis[sss][0]+e[i].cost){
                dis[v][0]=dis[sss][0]+e[i].cost;
                if(!vis[v]){
                    que.push(v);
                    vis[v]=1;
                }
            }
        }
    }
}

int main(){
//    freopen("move.in","r",stdin);
//    freopen("move.out","w",stdout);
    cin>>n>>m>>q>>k;
    for(int i=1;i<=m;++i){
        cin>>a>>b>>c;
        add(a,b,c);
    }
    for(int i=1;i<=q;++i){
        cin>>a>>b>>c;
        push(a,b,c);
    }
    if(k==1)SPFA();
    else if(!k)spfa();
    else Spfa();
    int ans=0x3f3f3f3f;
    for(int i=0;i<=1;++i)
        ans=min(ans,dis[n][i]);
    cout<<ans;
    return 0;
}

数据奇水
本来以为k肯定有小于边数大于1的数据
但是没有……所以乱搞(数据分治)就搞过去了
正解做法应该是dp
交上去的是个数据分治,因为30分部分分有个地方变量名写错了所以只得了45分

U14263 秀秀和哺噜国(cut)

题目描述

哺噜国里有!个城市,有的城市之间有高速公路相连。在最开始时,哺噜国里有n− 条高
速公路,且任意两座城市之间都存在一条由高速公路组成的通路。
由于高速公路的维护成本很高, 为了减少哺噜国的财政支出,将更多的钱用来哺育小哺噜,
秀秀女王决定关闭一些高速公路。 但是为了保证哺噜国居民的正常生活,
不能关闭太多的高速公路,要保证每个城市通过高速公路与至少$个城市(包括自己)相连。

在得到了秀秀女王的指令后,交通部长华华决定先进行预调研。
华华想知道在满足每个城市都可以与至少$个城市相连的前提下,
有多少种关闭高速公路的方案(可以一条也不关) 。两种方案不同,
当且仅当存在一条高速公路在一个方案中被关闭,
而在另外一个方案中没有被关闭。 由于方案数可能很大,
你只需输出不同方案数对786433取模后的结果即可。
其中786433 =
\(2^{17}\)+ 1。

输入输出格式

输入格式:

从文件cut.in 中读入数据。
输入第一行,包含两个正整数n,k。
接下来的n − 1行,每行包含两个正整数a和b,
表示城市a和城市b之间有一条高速公路相
连。

输出格式:

输出文件到cut.out 中。
输出一个非负整数,表示所求方案数对786433 取模后的结果。

输入输出样例

输入样例#1:

5 2
1 2
2 3
3 4
4 5

输出样例#1:

3

【样例 1 解释】

三种方案分别为:
一条高速公路也不关闭;
关闭城市2和城市3之间的高速公路;
关闭城市3和城市4之间的高速公路。

输入样例#2:

10 2
1 2
1 3
2 4
2 5
3 6
3 7
3 10
5 8
6 9

输出样例#2:

12

【子任务】

对于20%的数据:! ≤ 20;
另有30%的数据:! ≤ 100;
另有10%的数据:$ ≤ 100;
另有20%的数据:! ≤ 1000;
对于100%的数据:! ≤ 5000,$ ≤ !。

说明

空间限制:512MB

听说是个树形dp但是不会
233
题解大人说:
我们令dp[i][j]表示以i为根且当前联通块大小为k的方案总数,
特别的,dp[i][0]表示割点当前点与其父亲是棵树的方案总数。
对于u的一个孩子v可以得到转移方程dp[u][j+k]=dp[u][j]*dp[v][k]
另外$$dp[u][0]=\sum_{j=0}^{k} dp[u][j]$$
这样乍看是n3的,有一个技巧可以做到n2即每次dp时,
只枚举当前u所在子树的大小,每当枚举到它的其中孩子时,
当前u所在子树的大小加上它孩子为根的子树的大小。
可以理解为每一个点对只被枚举到一次。
最后答案即为dp[root][0]

#include<cstdio>
#include<cstdlib>
#define N 5555
#define M 786433
using namespace std;
typedef long long LL;

struct edge{
	int t,n;
}e[N*2];
LL h[N],size[N],f[N][N],g[N],cnt[N];
int n,K,tote;

void add(int u,int v){
	e[++tote].t=v;
	e[tote].n=h[u];
	h[u]=tote;
	return ;
}
void dfs(int u,int fa){
	size[u]++; f[u][1]=1;
	for (int i=h[u];i;i=e[i].n)
	{
		int v=e[i].t;
		if (v==fa) continue;
		dfs(v,u);
		for (int j=1;j<=size[u]+size[v];j++) g[j]=0;
		for (int j=1;j<=size[u];j++) g[j]=cnt[v]*f[u][j]%M;
		for (int j=1;j<=size[u];j++)
		for (int k=1;k<=size[v];k++) g[j+k]=(g[j+k]+f[u][j]*f[v][k]%M)%M;
		for (int j=1;j<=size[u]+size[v];j++) f[u][j]=g[j];
		size[u]+=size[v];
	}
	for (int i=K;i<=size[u];i++) cnt[u]=(cnt[u]+f[u][i])%M;
	return ;
}
int main(){
	freopen("cut.in","r",stdin);
	freopen("cut.out","w",stdout);
	scanf("%d %d",&n,&K);
	for (int i=1;i<n;i++){
		int u,v;
		scanf("%d %d",&u,&v);
		add(u,v); add(v,u);
	}
	dfs(1,1);
	printf("%d\n",cnt[1]);
	return 0;
}
posted @ 2017-10-23 14:59  Grary  阅读(245)  评论(0编辑  收藏  举报
博客园 首页 私信博主 编辑 关注 管理 新世界