2019.6.26 JZ DAY2总结

D A Y 2 DAY2 DAY2

T 1 T1 T1
C e n s o r e d ! Censored! Censored!

Description
给出p个长度不超过10的字符串,字符集大小为n。
如果这p个串都不是字符串s的子串,则认为s是幸运的。
求长度为m的幸运串个数

Input
第一行三个正整数n,m,p。
第二行n个不同的字符,表示字符集,其ASCII码大于32。
接下来p行每行一个字符串,表示不允许被包含的串。

Output
一行一个整数,表示幸运串个数。

Sample Input
2 3 1
ab
bb

Sample Output
5

Hint
【样例说明】
aaa aab aba baa bab 共5个串为幸运串。
【数据规模】
对于20%的数据,m ≤ 6,n ≤ 10;
对于50%的数据,p个串的长度不超过4,m ≤ 50,n ≤ 10;
对于100%的数据,n ≤ 50,m ≤ 50,p ≤ 10。

考场没有一点思路,直接弃疗。
考后知道正解是AC自动机+DP,说明并没有完全了解AC自动机的原理及运用。考后巩固AC自动机,可是感觉理解程度好像还没有初学那次那么深刻,花了大量的时间后算是比较理解AC自动机了。对于我的弱项DP,不过本题的DP转移还是比较良心的非常易懂。And这题不给取模,只能用大数加法,我发现我不会大数加法,只能暂时copy,赶在今明两天学会。
大概思路将所有的病毒串建一个AC自动机,然后在AC自动机上做DP。设 f [ i ] [ j ] f[i][j] f[i][j]表示取到第i个字母在AC自动机上的位置为j时的方案数,转移显然 f [ i + 1 ] [ t m p ] + = f [ i ] [ j ] f[i + 1][tmp] += f[i][j] f[i+1][tmp]+=f[i][j] tmp为枚举在j位置时加上一个枚举的字母k的位置。注意AC自动机的fail数组要下传即可。

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#define ll long long
using namespace std;

const int maxn = 55;
struct Edge{
	int vis[maxn * 10],id,fail,p;
} f[maxn * 11];
int n,m,p,asc[maxn * 10],cnt;
queue <int> q;

struct largeint
{
	ll num[110],len;
	largeint()
	{
		memset(num,0,sizeof(num)),len=1;
	}
	largeint operator=(const char s[])
	{
		len=strlen(s);
		for(int i = 0; i <= len -1; i ++) num[i] = s[len - i - 1] - '0';
		while (num[len-1]==0 && len>1) -- len;
		return *this;
	}
	largeint operator=(const ll x)
	{
		ll temp=x;
		len=1;
		do
		{
			num[len-1]=temp%10;
			temp/=10,len++;
		}
		while (temp);
		while (num[len-1]==0 && len>1)--len;
		return *this;
	}
	largeint operator+(const largeint &x)const
	{
		largeint temp;
		temp.len=max(len,x.len)+1;
		for (int i =0; i <= temp.len - 1; i ++)
		{
			temp.num[i] += num[i] + x.num[i];
			temp.num[i+1] = temp.num[i] / 10;
			temp.num[i] %= 10;
		}
		while (temp.num[temp.len-1]==0 && temp.len>1)--temp.len;
		return temp;
	}
	void prin()
	{
		for (int i = len - 1; i >= 0; i --) printf("%lld",num[i]);
		printf("\n");
	}
} v[maxn][maxn * 11];

void insert(string s)
{
	int root = 0;
	for (int i = 0; s[i]; i ++)
	{
		int id = asc[s[i]];
		if (!f[root].vis[id]) f[root].vis[id] = ++ cnt,root = cnt; else root = f[root].vis[id];
	}
	f[root].p = 1;
}

void Get_fail()
{
	for (int i = 1; i <= n; i ++) if (f[0].vis[i]) q.push(f[0].vis[i]),f[f[0].vis[i]].fail = 0;
	while (!q.empty())
	{
		int u = q.front();
		q.pop();
		if (f[f[u].fail].p) f[u].p = 1;
		for (int i = 1; i <= n; i ++)
		{
			if (f[u].vis[i]) f[f[u].vis[i]].fail = f[f[u].fail].vis[i],q.push(f[u].vis[i]); else f[u].vis[i] = f[f[u].fail].vis[i];
		}
	}
}

int main()
{
	scanf("%d%d%d",&n,&m,&p);
	scanf("\n");
	for (int i = 1; i <= n; i ++) asc[getchar()] = i;
	for (int i = 1; i <= p; i ++)
	{
		string s;
		cin >> s;
		insert(s);
	}
	Get_fail();
	v[0][0] = 1;
	for (int i = 0; i <= m - 1; i ++)
		for (int j = 0; j <= cnt; j ++)
			for (int k = 1,tmp; k <= n; k ++)
			{
				if (f[j].vis[k] != -1)
				{
					tmp = f[j].vis[k];
					if (!f[tmp].p) v[i + 1][tmp] = v[i + 1][tmp] + v[i][j];
				} else 
				{
					tmp = f[j].fail;
					while (tmp) {if (f[tmp].vis[k] != -1) break;tmp = f[tmp].fail;};
					if (tmp == -1) tmp = 0; else tmp = f[tmp].vis[k];
					if (!f[tmp].p) v[i + 1][tmp] = v[i + 1][tmp] +  v[i][j];
				}
			}
	largeint ans;
	for (int i = 0; i <= cnt; i ++) ans = ans + v[m][i];
	ans.prin();
	return 0;
}

T 2 T2 T2
f i n d    t h e   m o s t   c o m f o r t a b l e   r o a d find\ \ the\ most \ comfortable \ road find  the most comfortable road

Description
XX星有许多城市,城市之间通过一种奇怪的高速公路SARS(Super Air Roam Structure—超级空中漫游结构)进行交流,每条SARS都对行驶在上面的Flycar限制了固定的Speed,同时XX星人对 Flycar的“舒适度”有特殊要求,即乘坐过程中最高速度与最低速度的差越小乘坐越舒服 ,(理解为SARS的限速要求,flycar必须瞬间提速/降速,痛苦呀 ),
但XX星人对时间却没那么多要求。要你找出一条城市间的最舒适的路径。(SARS是双向的)。

Input
输入包括多个测试实例,每个实例包括:
第一行有2个正整数n 和m ,表示有N个城市和M条SARS。
接下来的行是三个正整数StartCity,EndCity,speed,表示从表面上看StartCity到EndCity,限速speedSARS。
接下来一行有2个正整数Start,End, 表示寻路的起终点。

Output
打印一行,仅输出一个非负整数表示最佳路线的舒适度最高速与最低速的差。如果起点和终点不能到达,那么输出-1。

Sample Input
4 4
1 2 2
2 3 4
1 4 1
3 4 2
1 3

Sample Output
1

Hint
【样例说明】
路径为1->4->3。
【数据规模】
对于30%的数据,输入数据是一棵树。
对于60%的数据,m ≤ 1000;
对于100%的数据,n ≤ 100,m ≤ n * (n + 1) / 2,边权 ≤ 1e9。

唉考场上遇到这样的题竟然没有切掉着实可惜,我甚至还记得在某谷上面做过原题。关键是考场的时候思路错误,然后还一直按这个思路深究没有考虑其他方法所以错了,考场用二分加spfa,我知道自己的正确性时错误的,自己都可以出数据hack掉,可竟然骗了54.5pts。正解最小生成树加并查集,水到不行。先将所有边按照边权从小到大拍,然后n2暴力即可。时间复杂度貌似过不了可是还是过了。

#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn = 110;
const int maxm = 5100;
struct Edge{
	int from,to,val;
} f[maxm << 1];
int n,m,s,t,cnt,fa[maxn],ans;

bool cmp(Edge a,Edge b) {return a.val < b.val;}

void pre()
{
	for (int i = 1; i <= n; i ++) fa[i] = i;
}

int get(int a)
{
	if (a == fa[a]) return a; else return fa[a] = get(fa[a]);
}

void solve()
{
	ans = 1e9 + 10;
	for (int i = 1; i <= cnt; i ++)
	{
		pre();
		for (int j = i,x,y; j <= cnt; j ++)
		{
			x = get(f[j].from),y = get(f[j].to);
			if (x != y) fa[x] = y;
			if (get(s) == get(t))
			{
				if (f[j].val - f[i].val < ans) ans = f[j].val - f[i].val;
				break;
			}
		}
	}
}

int main()
{
	while (scanf("%d%d",&n,&m) != EOF)
	{
		cnt = 0;
		for (int i = 1,u,v,w; i <= m; i ++)
		{
			scanf("%d%d%d",&u,&v,&w);
			f[++ cnt].from = u,f[cnt].to = v,f[cnt].val = w;
			f[++ cnt].from = v,f[cnt].to = u,f[cnt].val = w;
		}
		scanf("%d%d",&s,&t);
		sort(f + 1,f + 1 + cnt,cmp);
		solve();
		if (ans == 1e9 + 10) printf("%d",-1); else printf("%d",ans);
	}
	return 0;
}

T 3 T3 T3
G S S 6 GSS6 GSS6

Description
给出n个数a[i],要求你支持共m次操作,种类如下:
1.I x y : 在第x个数前插入一个数y;
2.D x : 删除第x个数;
3.R x y : 把第x个数改成y;
4.Q x y : 输出max{a[i] + a[i + 1] + … + a[j] | x<=i<=j<=y}。

Input
第一行一个整数n。
第二行n个整数表示a[i]。
第三行一个整数m。
接下来m行每行一个操作,格式见上。

Output
对于每个操作Q输出一行一个整数,表示相应答案。

Sample Input
5
3 -4 3 -1 6
10
I 6 2
Q 3 5
R 5 -4
Q 3 5
D 2
Q 1 5
I 2 -10
Q 1 6
R 2 -1
Q 1 6

Sample Output
8
3
6
3
5

Hint
【数据规模】
对于40%的数据,n,m ≤ 1000;
对于100%的数据, n,m ≤ 10W,A中元素 ≤ 1W,数列保证非空。

考场看到这种题就知道是一个我不会的数据结构题,直接上暴力。结果拿了45.5pts,这数据出的也真的是良心。
正解是平衡树,用splay和无旋treap均可,可我两个都不会,还得学,暂时先上暴力code。

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

const int maxn = 1e3 + 10;
int n,m,a[maxn],ans,now;

int max(int a,int b) {return a > b ? a : b;}

int main()
{
	scanf("%d",&n);
	for (int i = 1; i <= n; i ++) scanf("%d",&a[i]);
	scanf("%d",&m);
	for (int i = 1; i <= m; i ++)
	{
		char c;
		int x,y;
		cin >> c;
		if (c == 'I') 
		{
			cin >> x >> y;
			n ++;
			for (int i = n; i >= x + 1; i --) a[i] = a[i - 1]; 
			a[x] = y;
		}
		if (c == 'D') 
		{
			cin >> x;
			n --;
			for (int i = x; i <= n; i ++) a[i] = a[i + 1];
		}
		if (c == 'R') 
		{
			cin >> x >> y;
			a[x] = y;
		}
		if (c == 'Q')
		{
			cin >> x >> y;
			ans = -0x3f3f3f3f;
			for (int i = x; i <= y; i ++) ans = max(ans,a[i]);
			now = 0;
			for (int i = x; i <= y; i ++) 
			{
				now += a[i];
				if (now < 0) now = 0; else ans = max(ans,now);
			}
			printf("%d\n",ans);
		}
	}
	return 0;
}

昨天第一题的AC自动机复习了好久,所以没来得及打总结,晚一天补上。
昨日分数0+54.5+45.5=100,有进步,rank13,继续加油鸭。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值