T1
思路
trie,AC自动机,hash都可做。良心出题人
代码
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const int N = 200000 + 100;
int a[N][30];
ll read(){
ll x = 0,f = 1;char c = getchar();
while(c < '0' || c > '9'){
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
int tot,n,len;
int bz[N * 50];
void add(char *s) {
int now = 0;
for(int i = 0;i < len;++i) {
if(!a[now][s[i] - 'a']) a[now][s[i] - 'a'] = ++tot;
now = a[now][s[i] - 'a'];
}
bz[now] = 1;
}
int Len;
char SS[N];
int find(int x) {
int now = 0;
for(int i = x;i <= Len;++i) {
if(!a[now][SS[i] - 'a']) {
return 0;
}
now = a[now][SS[i] - 'a'];
if(bz[now]) return 1;
}
return 0;
}
char s[100];
int main() {
freopen("monitor.in","r",stdin);
freopen("monitor.out","w",stdout);
Len = read();n = read();len = read();
for(int i = 1;i <= n;++i) {
scanf("%s",s);
add(s);
}
scanf("%s",SS + 1);
for(int i = 1;i <= Len;++i) {
if(find(i)) {
printf("%d",i);
return 0;
}
}
puts("no");
return 0;
}
T2
20分思路
对于没有k = 0操作的分,可以直接利用前缀和来做,用sum[i]表示从节点i走到根节点所经过的路程。当查询x,y时,直接用sum[y] - sum[x]就是从x走到y所经过的路程
40分思路
对于只有一次k = 0操作的情况,整棵树中只有一个环。并且可以发现,因为整棵树时联通的,所以一定可以从某个位置走到这个环,走上几圈再回去。因为走过来又走回去,所以中间路程上的话费就抵消掉了。所以每次查询只要查询一下是否由x * k + w = t。k为那个环的大小,w为从x走到y的简单路径的长度,t为查询的时候所给出的t。
100分思路
其实通过40分思路已经可以发现些什么了。现在只不过是从一个环变成了n个环。那么上面那个式子就变成了,\(x_1 * k_1 + x_2 * k_2+...+x_n*k_n + w=t\)根据某某定理,这个式子有解的充要条件是\(gcd(x_1,x_2,x_3...x_n)|t-w\)。所以得出结论:这是一道数学题。
代码
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const int N = 100000 + 100,logN = 20;
ll read(){
ll x = 0,f = 1;char c = getchar();
while(c < '0' || c > '9'){
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
struct node {
int v,nxt;
ll w;
}e[N * 2];
int head[N],ejs;
void add(int u,int v,int w) {
e[++ejs].v = v;e[ejs].nxt = head[u];head[u] = ejs;e[ejs].w = w;
}
ll sum[N];
void dfs(int u,int father) {
for(int i = head[u];i;i = e[i].nxt) {
int v = e[i].v;
if(v == father) continue;
sum[v] = sum[u] + e[i].w;
dfs(v,u);
}
}
ll ab(ll x) {
return x > 0 ? x : -x;
}
ll gcd(ll a,ll b) {
return !b ? a : gcd(b, a % b);
}
ll g;
int main() {
freopen("lab.in","r",stdin);
freopen("lab.out","w",stdout);
int n = read(),q = read();
for(int i = 1;i < n; ++i) {
int u = read(), v = read(),w = read();
add(u,v,w);add(v,u,-w);
}
dfs(1,0);
while(q--) {
int bz = read(),x = read(),y = read(),w = read();
if(bz == 0) {
if(!g) g = ab(sum[x] - sum[y] + w);
else g = gcd(g,ab(sum[x] - sum[y] + w));
}
else {
if(sum[y] - sum[x] == w) puts("yes");
else if(g == 0) puts("no");
else if((w - (sum[y] - sum[x])) % g == 0) puts("yes");
else puts("no");
}
}
return 0;
}
/*
7 5
1 2 1
1 3 5
2 4 3
2 5 4
3 6 8
3 7 7
0 4 5 7
0 4 6 15
0 5 6 18
0 1 4 5
0 3 4 5
*/
T3
思路
思路其实并不难,进行两边dfs,分别处理一些东西。考场上写完这道题就再也不想讲思路了。看代码理解吧
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const int N = 100000 + 100;
ll read(){
ll x = 0,f = 1;char c = getchar();
while(c < '0' || c > '9'){
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
ll ans[N][2];
int n,Q;
struct node {
int u,v,nxt,w;
}e[N * 2];
int head[N],ejs;
void add(int u,int v,int w) {
e[++ejs].v = v;e[ejs].w = w;e[ejs].nxt = head[u];head[u] = ejs;
}
queue<int>q;
ll dis[N];
void bfs(int U) {
while(!q.empty()) q.pop();
memset(dis,-1,sizeof(dis));
q.push(U);
dis[U] = 0;
while(!q.empty()) {
int u = q.front();
q.pop();
for(int i = head[u];i;i = e[i].nxt) {
int v = e[i].v;
if(dis[v] != -1) continue;
dis[v] = dis[u] + e[i].w;
if(dis[v] & 1) ans[U][1] += dis[v];
else ans[U][0] += dis[v];
q.push(v);
}
}
return;
}
void BF1() {
for(int i = 1;i < n;++i) {
int u = read(),v = read(),w = read();
add(u,v,w);
add(v,u,w);
}
for(int i = 1;i <= n;++i) bfs(i);
while(Q--) {
int x = read();
printf("%lld %lld\n",ans[x][1],ans[x][0]);
}
return;
}
ll num[N][2];//到每个节点为奇数偶数的点得个数。
ll nnum[N][2];
ll f[N][2];//在当前子树中到当前点为偶数奇数得大小
//ans[N][2]为真正答案
void dfs1(int u,int father) {
for(int i = head[u];i;i = e[i].nxt) {
int v = e[i].v;
if(v == father) continue;
dfs1(v,u);
int w = e[i].w;
if(w & 1) {
num[u][0] += num[v][1];
num[u][1] += num[v][0] + 1 ;
f[u][0] += num[v][1] * w + f[v][1];
f[u][1] += (num[v][0] + 1) * w + f[v][0];
}
else {
num[u][0] += num[v][0] + 1;
num[u][1] += num[v][1];
f[u][0] += (num[v][0] + 1) * w + f[v][0];
f[u][1] += num[v][1] * w + f[v][1];
}
}
}
void dfs2(int u,int father) {
for(int i = head[u];i;i = e[i].nxt) {
int v = e[i].v;
if(v == father) continue;
ll w = e[i].w;
if(w & 1) {
nnum[v][1] = num[v][1] + nnum[u][0] - num[v][1] + 1;
nnum[v][0] = num[v][0] + nnum[u][1] - num[v][0] - 1;
ans[v][1] = f[v][1] + (ans[u][0] - (num[v][1] * w + f[v][1])) + (nnum[v][1] - num[v][1]) * w;
ans[v][0] = f[v][0] + (ans[u][1] - ((num[v][0] + 1) * w + f[v][0])) + (nnum[v][0] - num[v][0]) * w;
}
else {
nnum[v][1] = num[v][1] + nnum[u][1] - num[v][1];
nnum[v][0] = num[v][0] + nnum[u][0] - num[v][0] - 1 + 1;
ans[v][1] = f[v][1] + (ans[u][1] - (num[v][1] * w +f[v][1])) + (nnum[v][1] - num[v][1]) * w;
ans[v][0] = f[v][0] + (ans[u][0] - ((num[v][0] + 1) * w + f[v][0])) + (nnum[v][0] - num[v][0]) * w;
}
dfs2(v,u);
}
}
void BF2() {
for(int i = 1;i < n; ++i) {
int u = read(),v = read(),w = read();
add(u,v,w);add(v,u,w);
}
dfs1(1,0);
nnum[1][1] = num[1][1];
nnum[1][0] = num[1][0];
ans[1][1] = f[1][1];
ans[1][0] = f[1][0];
dfs2(1,0);
while(Q--) {
int x = read();
printf("%lld %lld\n",ans[x][1],ans[x][0]);
}
}
int main() {
freopen("civilization.in","r",stdin);
freopen("civilization.out","w",stdout);
n = read(),Q = read();
if(n <= 2000) {
BF1();
return 0;
}
BF2();
return 0;
}
/*
4 4
1 2 1
2 3 2
2 4 3
1 2 3 4
*/
总结
期望得分: 100 + 20 + 100 = 220
实际得分: 100 + 0 + 100 = 200
t1trie的空间算错了RE了一个点。trie的空间是NLL,n是字符串的个数,L是最长字符串的长度。最后半个小时T2打了个暴力结果忘了边反着走的时候边权为反的。T3,哎。自己都不相信能在考场上码出来
一言
一个人因为遭遇失败,才会拥有从那里再站起来的强悍。