01:http://acm.hdu.edu.cn/showproblem.php?pid=6161
02:http://acm.hdu.edu.cn/showproblem.php?pid=6162
03:http://acm.hdu.edu.cn/showproblem.php?pid=6163
04:http://acm.hdu.edu.cn/showproblem.php?pid=6164
05:http://acm.hdu.edu.cn/showproblem.php?pid=6165
06:http://acm.hdu.edu.cn/showproblem.php?pid=6166
07:http://acm.hdu.edu.cn/showproblem.php?pid=6167
08:http://acm.hdu.edu.cn/showproblem.php?pid=6168
09:http://acm.hdu.edu.cn/showproblem.php?pid=6169
10:http://acm.hdu.edu.cn/showproblem.php?pid=6170
本场比赛还是一样的发挥,3题水平,可惜10的模拟我没搞出来。
题目意思是给定一颗有n个节点的树,和点的点权。有m次查询,每次查询范围在x节点到y节点之间,当沿最短路径走经过的点权在a - b的范围内的时候,最终结果就加上这个点权。
那么做过LCA的同学可以立马发现这就是经典的求公共祖先的问题,所以我们每次的路径其实就是求LCA到两个节点的路径,然后预处理所有点到根节点所经历的点,每次询问直接三个点的经历的点判断一遍就好了。
#include
typedef long long ll;
const int maxn = 100000+111;
using namespace std;
int dp[maxn<<1][25];
int ver[maxn<<1];
int dep[maxn<<1];
int quan[100010];
int vis[maxn];
vector<int> dir[maxn];
int first[maxn];
int head[maxn];
int tot,cnt;
struct Edge
{
int to,next;
}edge[maxn<<1];
void addedge(int u,int v)
{
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot++;
}
void init(int n)
{
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++)
dir[i].clear();
cnt = tot = 0;
}
void dfs(int u,int dfn)
{
vis[u]=1;ver[++cnt] = u;first[u]=cnt;dep[cnt]=dfn;
for(int i = head[u];~i;i=edge[i].next)
{
int v = edge[i].to;
if(!vis[v])
{
dir[v].assign(dir[u].begin(),dir[u].end());
dir[v].push_back(quan[v]);
dfs(v,dfn+1);
ver[++cnt] = u;
dep[cnt] = dfn;
}
}
}
void ST(int n)
{
int k = int (log2(n));
for(int i=1;i<=n;i++) dp[i][0]=i;
for(int j=1;j<=k;j++)
for(int i=1;i+(1<1<=n;i++)
{
int a = dp[i][j-1];
int b = dp[i+(1<<(j-1))][j-1];
if(dep[a]else dp[i][j]=b;
}
}
int RMQ(int l,int r)
{
int k = int(log2(r-l+1.0));
int a = dp[l][k];
int b = dp[r-(1<1][k];
if(dep[a]return a;
return b;
}
int LCA(int u,int v)
{
int x = first[u], y = first[v];
if(x>y) swap(x,y);
return ver[RMQ(x,y)];
}
int main()
{
int n,m;
while(~scanf("%d %d",&n,&m))
{
init(n);
for(int i=1;i<=n;i++)
scanf("%d",&quan[i]);
for(int i=1;i<=n-1;i++)
{
int a,b;
scanf("%d %d",&a,&b);
addedge(a,b);
addedge(b,a);
}
dir[1].push_back(quan[1]);
dfs(1,1);ST(2*n-1);
while(m--)
{
int s,t,a,b;
scanf("%d%d%d%d",&s,&t,&a,&b);
int v=LCA(s,t);
ll sum=0;
for(int i=0;iif(dir[s][i]<=b&&dir[s][i]>=a)
sum+=dir[s][i];
}
for(int i=0;iif(dir[t][i]<=b&&dir[t][i]>=a)
sum+=dir[t][i];
}
for(int i=0;iif(dir[v][i]<=b&&dir[v][i]>=a)
sum-=2*dir[v][i];
}
if(quan[v]>=a&&quan[v]<=b)
sum+=quan[v];
printf("%I64d",sum);
if(m==0) printf("\n");
else printf(" ");
}
}
return 0;
}
这题的意思就是让所有两个点之间都有一条连通的道路。
那么就从前往后搜,当搜一个点时发现它不能到其中一个点,那么就反过来搜看看那个点能不能到它,dfs即可。
#include
using namespace std;
bool vis[1010];
vector<int >a[1010];
void dfs(int s)
{
vis[s]=1;
for(int i=0;iif(vis[a[s][i]]==0) dfs(a[s][i]);
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m;
scanf("%d %d",&n,&m);
int x,y;
for(int i=1;i<=n;i++)
a[i].clear();
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
a[x].push_back(y);
}
bool flag=1;
for(int i=1;imemset(vis,0,sizeof(vis));
dfs(i);
vector<int>v;
for(int j=i+1;j<=n;j++)
{
if(vis[j]==0)
{
v.push_back(j);
}
}
for(int j=0;jmemset(vis,0,sizeof(vis));
dfs(v[j]);
if(vis[i]==0)
{
flag=0;
break;
}
}
if(flag==0) break;
}
if(flag==1) printf("I love you my love and our love save us!\n");
else printf("Light my fire!\n");
}
}
这题的意思就是一个有n个元素的a序列,每两个元素相加组成有n*(n - 1) / 2个元素的b序列,现在将a序列和b序列混杂,问原来的a序列是什么。
可以知道,大的数都是由小的数推出来的,所以从小到大排序并记录当前数出现的个数。从小到大跑一遍,当前数的个数不为0时就把此数和之前放进答案的数相加的数的次数减一,最后把此数放入答案,而最后当放满sqrt(2 * m + 0.25)个就是答案。
#include
using namespace std;
int stand[125255];
int ans[1000];
map<int,int>M;
int main()
{
int m;
while(scanf("%d",&m) != EOF)
{
M.clear();
int n=sqrt(2*m+0.25);
memset(ans, 0, sizeof(ans));
for(int i=1;i<=m;i++)
scanf("%d", &stand[i]), M[stand[i]]++;
if(m==0) cout<<"0"<else if(m==1)
{
cout<<"1"<cout<1]<else
{
sort(stand + 1, stand + 1 + m);
int start=3;
int cnt=2;
ans[1]=stand[1],ans[2]=stand[2];
M[ans[1]+ans[2]]--;
while(1)
{
if(cnt==n) break;
for(int i=start;i<=m;i++)
{
if(M[stand[i]]!=0)
{
M[stand[i]]--;
start=i + 1;
break;
}
}
ans[++cnt]=stand[start - 1];
for(int i=1;iprintf("%d\n",n);
for(int i=1;i<=cnt;i++)
{
printf("%d",ans[i]);
if(i==cnt) printf("\n");
else printf(" ");
}
}
}
}
题目给定两个字符串,第一个字符串只有字母,第二个里面包含”.“和”“,”.“可以代表任意字符,”“可以代表把前面那个字母删掉,或者是空或者任意个前面那个字符的重复。
比赛的时候想着模拟,赛后看了网上题解代码才发现是dp…….直接dp[i][j]代表去匹配的字符串到第i个的时候,被匹配字符串到第j个的时候能否匹配的上即可。
ps:网上还有用regex的大神,学习了。
#include
using namespace std;
char s1[5000], s2[5000];
int dp[2510][2510];
int main()
{
int t;
cin>>t;
while(t--)
{
memset(dp, 0, sizeof(dp));
scanf("%s%s", s1 + 1, s2 + 1);
int len1 = strlen(s1 + 1), len2= strlen(s2 + 1);
dp[0][0] = 1;
for(int i = 1;i <= len2;i++)
{
if(s2[i] == '*'&&i == 2)
dp[i][0] = 1;
for(int j = 1;j <= len1;j++)
{
if(s2[i] == '.')
dp[i][j] = dp[i - 1][j - 1];
else if(s2[i] != '*')
{
if(s2[i] == s1[j])
dp[i][j] = dp[i - 1][j - 1];
}
else
{
dp[i][j] = max(dp[i - 1][j], dp[i - 2][j]);
if(dp[i][j - 1]&&s1[j - 1] == s1[j])
dp[i][j] = 1;
}
}
}
if(dp[len2][len1])
printf("yes\n");
else
printf("no\n");
}
return 0;
}