这是一套大水题,8人AK,我100+0+0=100 rank29
T1乱搞题
T2树形DP,我不知道哪里搞炸了
T3更水,表示看到仙人掌就没去想了(其实是没想出)
看题解或代码的看目录,有索引。
T1
问题 A: 寻找羔羊
时间限制: 1 Sec 内存限制: 256 MB
题目描述
给定一个由小写字母组成的字符串,寻找包含“agnus”(羔羊)的子串的个数。注意:当且仅当两个子串的起始位置和终点不同时,这两个子串属于不同的子串。
输入
只有一个字符串,表示题中所述的字符串。
输出
仅一个数字,表示满足题意的子串个数。
样例输入
agnusbgnus
样例输出
6
提示
【样例解释】
6个子串分别是:agnus、agnusb、agnusbg、agnusbgn、agnusbgnu、agnusbgnus。
【数据规模和约定】
对于 40%的数据,字符串长度<=1000
对于 100%的数据,字符串长度<=30000
Solution
从头到尾遍历一次字符串,每碰到一次“agnus”,用“agnus”前面的字符数目乘上“agnus”后面的字符数目,即为含有当前“agnus”的字符串数目。
直接统计即可,不过要注意去重。
我们设找到的开头为x[i],末尾为y[i]=x[i]+4;
则个数为(x[i-1]~x[i]-1)*(y[i]+1~n),自己画一下图就行了
Code
#include <cstdio>
#include <cstring>
char s[30010];
int i,n,ans,x;
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
ans=x=0;
for (i=1;i+4<=n;i++)
if (s[i]=='a'&&s[i+1]=='g'&&s[i+2]=='n'&&s[i+3]=='u'&&s[i+4]=='s')
{ans+=(i-x)*(n-i-3); x=i;}
printf("%d\n",ans);
return 0;
}
T2
问题 B: 统计损失
时间限制: 1 Sec 内存限制: 512 MB
题目描述
SJY有一天被LLT紧急召去计算一些可能的损失。LLT元首管理的SHB国的交通形成了一棵树,现在将会出现一颗陨石砸在SHB国中,并且陨石砸毁的必定是SHB国构成的交通树上的一条路径。SHB国的损失可表示为被砸毁的路径上的所有城市价值之积。现在还暂时无法确定陨石的掉落路线,所以LLT元首希望SJY能够告诉他SHB国在受到每一种砸毁方式后会受到的损失之和模10086之后的值。注意:单独一个节点也被认为是合法的路径。
输入
第1行一个数n,表示城市数。
第2行n个数,第i个数表示第i个城市的价值。
第3到n+1行,每行两个数u,v,表示城市u,v之间有一条道路。
输出
包含一个数,表示SHB国将受到的损失之和。
样例输入
5
7 6 6 1 1
1 2
2 3
2 4
1 5
样例输出
778
提示
【数据规模和约定】
n<=100;
n<=3000;
n<=100000
Solution
题意即统计所有的路径权值积的和。
算法1:
DFS每个点的路径,统计答案,时间复杂度O(n^2)
算法2:
树形DP,统计每个点的子树到当前点的路径积的和,再DFS逆序计算并合并答案即可,时间复杂度O(n)。合并时需注意合并一次的效率,要保证能够做到O(子树个数)合并,并且需要注意10086不能求逆元 。
PS:这道题目我想的是LCA求路径那样的算法,结果需要求逆元……卡死
Code
#include<bits/stdc++.h>
using namespace std;
#define MOD 10086
#define ll long long
const ll MAXN=100010;
ll Head[MAXN],Next[MAXN*2],To[MAXN*2];
ll a[MAXN],sum[MAXN];
ll n,tot,ans;
void add_eage(ll x,ll y)
{
tot++;
Next[tot]=Head[x];
Head[x]=tot;
To[tot]=y;
}
void dfs(ll u,ll pre)
{
ans+=a[u]; sum[u]=0;
for (ll i=Head[u];i;i=Next[i])
{
ll v=To[i];
if (v==pre) continue;
dfs(v,u);
ans=(ans+sum[u]*sum[v]*a[u])%MOD;
sum[u]=(sum[u]+sum[v])%MOD;
}
ans=(ans+sum[u]*a[u])%MOD;
sum[u]=(sum[u]*a[u]+a[u])%MOD;
}
int main()
{
scanf("%lld",&n);
for (ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
for (ll i=1;i<n;i++)
{
ll x,y;
scanf("%lld%lld",&x,&y);
add_eage(x,y); add_eage(y,x);
}
ans=0;
dfs(1,0);
printf("%lld",ans);
return 0;
}
T3
问题 C: 简单题
时间限制: 1 Sec 内存限制: 512 MB
题目描述
dzy 手上有一张n 个点m 条边的联通无向图,仙人掌是一张每条边最多在一个简单环内的联通无向图。他想求这个无向图的生成仙人掌中最多有多少条边。
但是dzy 觉得这个问题太简单了,于是他定义了“美丽的生成仙人掌”,即在一个生成仙人掌中如果满足对于任意编号为i,j(i < j) 的两点,存在一条它们之间的简单路径上面有j-i+1 个点,则这个仙人掌是美丽的。
他现在想要知道这张图的美丽的生成仙人掌中最多有多少条边,你能帮帮他吗?
输入
第一行两个整数n,m。接下来m 行每行两个整数ui,vi,表示这两个点之间有一条无向边。保证图中没有自环。
输出
仅一行一个整数表示答案。
样例输入
2 1
1 2
样例输出
1
提示
【数据规模和约定】
对于10% 的数据,n <=10。
对于30% 的数据,n <=10^3。
对于100% 的数据,n <=10^5,m <= 2n。
Solution
算法1:
直接暴力枚举所有可能的边集,复杂度O(2n),期望得分10分
算法2:
研究题目可以发现,美丽的生成仙人掌中i 和i+ 1中必定有一条边。所以问题变成了:我们在一条链上加上若干条边使得得到的图是仙人掌且边数量最大。
显而易见,每一条非链边对应了链上的一个区间。于是问题就变成了选出最多数量的线段使得其互不相交。
这个可以用dp来实现,令F[i]为右端点最大为i 的区间的答案。则有:
F[i =max{F[j]+ 1(i和j之间存在一条边),f[i-1]}
时间复杂度O(n),期望得分100分,当然如果你一不小心写次了,时间复杂度O(n2),期望得分30分。
算法3:
显而易见,我们要求的是区间图的最大独立集。由于区间图把区间按右端点排序就是完美消除序列,直接贪心就可以了。时间复杂度O(nlogn)(用桶排可以做到O(n)),期望得分100分
PS:关键是用i,i+1代替i,j,我压根没想到
CODE
DP
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100010;
int Head[MAXN],Next[MAXN*2],To[MAXN*2];
bool flag[MAXN];
int f[MAXN],n,m,tot;
void add_eage(int x,int y)
{
tot++;
Next[tot]=Head[x];
Head[x]=tot;
To[tot]=y;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
if (x>y) swap(x,y);
if (y==x+1 && !flag[x]) { flag[x]=true;continue;}
add_eage(y,x);
}
for (int i=1;i<=n;i++)
{
f[i]=f[i-1];
for (int j=Head[i];j;j=Next[j])
f[i]=max(f[i],f[To[j]]+1);
}
printf("%d\n",f[n]+n-1);
return 0;
}
贪心
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100010;
int n,m,ans,cnt,r;
bool flag[MAXN];
struct eage
{
int x,y;
}e[MAXN*2];
bool cmp(eage a,eage b)
{
return a.y<b.y;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
if (x>y) swap(x,y);
if (y==x+1 && !flag[x]) { flag[x]=true;continue;}
cnt++;
e[cnt].x=x; e[cnt].y=y;
}
sort(e+1,e+1+cnt,cmp);
ans=n-1; r=0;
for (int i=1;i<=cnt;i++)
if (r<=e[i].x) { ans++; r=e[i].y; }
printf("%d",ans);
return 0;
}