Wannafly10 题解
- 一、签到题
交了几发0%不知道是不是题目有问题
后来再改代码发现,a*(100-x)这里可能爆int,~。~
#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
ll f(ll a,int x)
{
return a*(100-x)/100;
}
int main()
{
int a,b,c;
ll n;
scanf("%lld%d%d%d",&n,&a,&b,&c);
int aa = f(f(f(n,a),b),c);
int bb = f(f(f(n,a),c),b);
int cc = f(f(f(n,b),a),c);
int dd = f(f(f(n,b),c),a);
int ee = f(f(f(n,c),a),b);
int ff = f(f(f(n,c),b),a);
int ans = aa;
//cout<<aa<<' '<<bb<<' '<<cc<<' '<<dd<<' '<<ee<<' '<<ff<<endl;
ans = min(ans,bb);
ans = min(ans,cc);
ans = min(ans,dd);
ans = min(ans,ee);
ans = min(ans,ff);
printf("%d\n",n-ans);
return 0;
}
- 二、
暴力超时了,明天再看看别人写的
下面是做法最简单的代码,直接用双指针往后跑,如果一个字符被匹配了就直接指针向后移动
这里就要考虑一种情况,如果待匹配的字符是a,然后这一行的密码是a#,那么可以选择匹配或者不匹配。
仔细思考,如果一个密码是可以表示的,那么这个密码的每一个字符都可以在一行里找出,也就是在i和i后找到,所以如果i行有匹配的字符,那么选择匹配是不会影响到后面的字符匹配的,当然选择不匹配也有可能成功,不过不用考虑。
#include <bits/stdc++.h>
using namespace std;
#define FIN freopen("in.txt","r",stdin)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define Fi first
#define Se second
typedef long long LL;
typedef pair<int, int>pii;
int n,m,q;
char ch[30],str[10005];
bool vis[301][30];
int main(){
//FIN;
scanf("%d%d%d",&n,&m,&q);
for(int i=0;i<n;i++){
scanf("%s",ch);
for(int j=0;j<m;j++){
if(ch[j]=='#') vis[i][0]++;
else vis[i][ch[j]-'a'+1]++;
}
}
while(q--){
scanf("%s",str);
int len=strlen(str);
if(len>n)
puts("NO");
else{
int now=0;
for(int i=0;i<n&&now<len;i++){
if(vis[i][str[now]-'a'+1]){
now++;
}
}
printf("%s\n",now<len?"NO":"YES");
}
}
return 0;
}
这样看来这道题目难度还远远不及普通的包含通配符的字符串匹配。
- 三、题目
压缩版题意是:给一颗无根树,Q个查询,每次查询 先轰炸这个点,然后输出这个点目前为止 被轰炸的次数,(每个点被轰炸时,会波及距离2以内的点,也就是 点点之间的边数<=2的点)
- 开始题目没说清楚轰炸结束是一次轰炸结束,并且数据比较大750000,我就把所有轰炸离线了,最后查询。
思路是对于每个点,向上更新一次fa[i]和fa[fa[i]]的信息,最后再按照查询输出。这样复杂度是o(n)
- 开始题目没说清楚轰炸结束是一次轰炸结束,并且数据比较大750000,我就把所有轰炸离线了,最后查询。
#define mem(x) memset(x,0,sizeof(x))
using namespace std;
const int N = 750010;
int dp[N][2];//[0]记录总贡献,[1]记录子节点的贡献
int A[N];//记录[i]的直接轰炸次数
vector<int> tree[N];
void init()
{
mem(dp);mem(fa);mem(vis);
}
/*
*搜索得到father数组
*/
int fa[N];
bool vis[N];
void dfs(int u)
{
vis[u]=true;
for(int i=0;i<tree[u].size();i++)
{
int v = tree[u][i];
if(!vis[v])
{
vis[v]=true;
dfs(v);
fa[v]=u;
}
}
}
int n,q;
int main()
{
while(~scanf("%d%d",&n,&q))
{
init();
int a,b;
for(int i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
tree[a].push_back(b);
tree[b].push_back(a);
}
for(int i=1;i<=q;i++){
scanf("%d",&x);
A[x]++;
}
A[0]=0;
dfs(1);//默认1是根,不影响结果。
for(int i=2;i<=n;i++)
{
dp[fa[i]][1]+=A[i];//记录子节点的贡献
//互相贡献
dp[fa[i]][0]+=A[i];
dp[fa[fa[i]]][0]+=A[i];
dp[i][0]+=A[fa[fa[i]]]+A[fa[i]];
}
for(int i=1;i<=n;i++)//还要加上所有兄弟节点以及自己的轰炸次数
{
dp[i][0]+=dp[fa[i]][1];
}
//最后按照查询输出即可
}
return 0;
}
写出来发现样例都过不了,
那按照题意,然后只能每次查询遍历所有邻接点。。
那要是数据极端一点就会超时啊
然后改了一下,提交竟然过了~~
如果是边查询边输出的话,就当做图来思考,dp数组记录当前邻接点的和,
x和v相邻
ans = A[x]+sigma(dp[v]-A[x] + A[v]) 自己的次数+ 距离为2 + 距离为1的次数
using namespace std;
const int N = 750010;
int dp[N];
int A[N];
vector<int> tree[N];
void init()
{
mem(dp);
mem(A);
for(int i=0;i<N;i++)
tree[i].clear();
}
int n,q;
int main()
{
while(~scanf("%d%d",&n,&q))
{
init();
int a,b;
for(int i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
tree[a].push_back(b);
tree[b].push_back(a);
}
A[0]=0;
for(int i=1;i<=q;i++)
{
int x;
scanf("%d",&x);
A[x]++;
int ans=A[x];//自己的次数
for(int j=0;j<tree[x].size();j++)
{
int v=tree[x][j];
dp[v]++;//边为1的点加一次
ans+=dp[v]+A[v]-A[x];//加上邻结点的邻结点的次数,记得减去自己。
}
printf("%d\n",ans);
}
}
return 0;
}
- 四
可能是一道线段树的题,明天补
1e5的数据量来看,线段树无疑,然后维护的是最大有效(连续)子区间和,就想起了以前写的,分治法解决最大连续子区间和,
不过问题是,在在线处理的时候,如何对分治进行结合线段树的合并。
- 原博客的处理是遍历一遍n进行合并,一次处理的复杂度就是n,加起来就是m*logn * n,这道题明显不行。
那其实上述合并的操作就是,把左区间的右连续+有区间的左连续和 合并(如果左右区间连续) - 那维护的时候就要同时记录lsum,和rsum
- ssum好说 : ssum[rt]=左右连续? rsum[rt<<1]+lsum[rt<<1|1] : max(ssum[rt<<1],ssum[rt<<1|1])
- 然后问题就变成了,怎么更新lsum和rsum
- 我们先看lsum的定义,左端点连续区间和。
- 那至少得包括左端点,不是左区间的lsum,就是左区间的全部加上右区间的lsum
- lsum[rt] = max(lsum[rt<<1],sum[rt<<1]+lsum[rt<<1|1]) 当然后者的前提还是连续
- 那至此我们要维护4个值,sum,ssum,lsum,rsum
- 判断是否左右是否连续就 把 m 和m+1拿来判断一下就ok了
- 原博客的处理是遍历一遍n进行合并,一次处理的复杂度就是n,加起来就是m*logn * n,这道题明显不行。
到此这个问题应该是出来了
先来一发
//第一次写这种肝了1个多小时,注释里写一些写题目
#include<bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
int n,m;
const int N=1e5;
int A[N];
const int INF = 0x3ffffff;
typedef long long ll;
ll ssum[N<<2];
ll sum[N<<2];
ll lsum[N<<2];
ll rsum[N<<2];
//查询函数需要返回结构体才行
//这里可以把上面4个东西写入结构体,我这里开始没发现,所以就没改,就是用一个临时的node存放
typedef struct node{
ll sum,lsum,rsum,ssum;
node()
{
sum=lsum=rsum=ssum = -INF;
}
}Node;
//判断左右是否连续
bool isok(int a,int b)
{
//a^b 奇奇偶偶2进制第一位都是0
a = a>0?a:-a;
b = b>0?b:-b;
return (a^b)&1 ;
}
//这里参考上面的解释
void PushUp(bool ok,int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
ssum[rt]=max(ssum[rt<<1],ssum[rt<<1|1]);
lsum[rt]=lsum[rt<<1];
rsum[rt]=rsum[rt<<1|1];
if(ok)
{
lsum[rt]=max(lsum[rt],sum[rt<<1]+lsum[rt<<1|1]);
rsum[rt]=max(rsum[rt],rsum[rt<<1]+sum[rt<<1|1]);
ssum[rt]=max(ssum[rt],rsum[rt<<1]+lsum[rt<<1|1]);
}
}
//分治都要考虑是否连续
void build(int l,int r,int rt)
{
if(l==r)
{
sum[rt]=lsum[rt]=rsum[rt]=ssum[rt]=A[l];
}
else
{
int m=l+(r-l)/2;
build(lson);
build(rson);
bool fg = isok(A[m],A[m+1]);
PushUp(fg,rt);
}
}
void update(int L,int C,int l,int r,int rt)
{
if(l==r)
{
sum[rt]=lsum[rt]=rsum[rt]=ssum[rt]=C;
}
else
{
int m=l+(r-l)/2;
if(L<=m)update(L,C,lson);
else update(L,C,rson);
bool fg = isok(A[m],A[m+1]);
PushUp(fg,rt);
}
}
//由于查询没有pushup,所以要把4个数据写入node中来作为返回值
Node query(int L,int R,int l,int r,int rt)
{
Node ret;
if(L<=l && r<=R)
{
ret.sum = sum[rt];
ret.lsum = lsum[rt];
ret.rsum = rsum[rt];
ret.ssum = ssum[rt];
return ret;
}
else
{
int m=l+(r-l)/2;
Node left,right;
bool fg = isok(A[m],A[m+1]);
if(L<=m)
left=query(L,R,lson);
if(R>=m+1)
right=query(L,R,rson);
ret.sum=left.sum+right.sum;
ret.ssum = max(left.ssum,right.ssum);
ret.lsum = left.lsum;
ret.rsum = right.rsum;
if(fg)
{
ret.ssum = max(ret.ssum,left.rsum+right.lsum);
ret.lsum = max(ret.lsum,left.sum+right.lsum);
ret.rsum = max(ret.rsum,left.rsum+right.sum);
}
return ret;
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i=1;i<=n;i++)scanf("%d",A+i);
int op,x,y;
build(1,n,1);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&op,&x,&y);
cout<<op<<' '<<x<<' '<<y<<endl;
if(op)update(x,y,1,n,1);
else printf("%lld\n",query(x,y,1,n,1).ssum);
}
}
return 0;
}
然后这个程序只过了样例,肝不动了,回头再看。