题解:
水题啊,排个序取奇数位的数就可以了。
代码:
#include
#include
using namespace std;
typedef long long ll;
const ll size = 100005;
ll n,ans = 0;
ll da[2*size];//数组大小啊!!!
int main()
{
scanf("%lld",&n);
for (ll i = 1;i <= 2 * n;i++)
scanf("%lld",&da[i]);
sort(da + 1 , da + 1 + 2*n);
for (ll i =1;i<=2*n;i+=2)
ans += da[i];
printf("%lld",ans);
return 0;
}
题解:
不难啊,考虑到一条边的两端的点 x,y 的 b 值(设 x 为 y 的父节点),可以得出其差值为 ∑n1a[i]−∑y的子树a[i] 那么就好做了。首先,若知道 a 数组,则可以用此公式,先暴力算出根的 b ,然后用这个公式不停向下更新。若知道 b ,那么考虑叶节点y,其 ∑y的子树a[i] 恰好为其本身,然后就可以用一个只关于 ∑1na[i] 的式子表示,然后把其更新到父节点,然后一直更新到根节点,然后根节点的子树又为 ∑0na[i] 就可以解方程啦,哈哈,只用两次 dfs 。
代码(点不进去了,就用了标程):
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=100010;
ll T,t,n,edgenum,head[N],c[N],fa[N],dep[N],atot,ans[N],v[N];
struct edge {
int v,next;
} e[N<<1];
struct node {
ll tot,con;
} tr[N];
ll read() {
ll x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
void addedge(int f,int t) {
e[++edgenum].v=t;
e[edgenum].next=head[f];
head[f]=edgenum;
}
void dfs1(int a,int pre) {
fa[a]=pre;
dep[a]=dep[pre]+1;
for(int i=head[a]; i; i=e[i].next) {
if(e[i].v==pre) continue;
dfs1(e[i].v,a);
}
}
inline void solve1() {
ll root_tot=0,root_con=0,sum=0;
dep[0]=-1;
dfs1(1,0);
for(int i=2; i<=n; i++) {
tr[i].con=c[fa[i]]-c[i];
tr[i].tot=1;
tr[fa[i]].tot--;
tr[fa[i]].con-=tr[i].con;
}
for(int i=2; i<=n; i++) {
root_tot+=tr[i].tot*dep[i];
root_con+=tr[i].con*dep[i];
}
atot=(2*c[1]-root_con)/root_tot;
for(int i=2; i<=n; i++) ans[i]=(tr[i].con+tr[i].tot*atot)/2,sum+=ans[i];
ans[1]=atot-sum;
}
void dfs2(int a,int pre) {
fa[a]=pre;
dep[a]=dep[pre]+1;
for(int i=head[a]; i; i=e[i].next) {
if(e[i].v==pre) continue;
dfs2(e[i].v,a);
v[a]+=v[e[i].v];
}
}
void dfs3(ll sum,int a,int pre) {
ans[a]=(sum-(v[a]<<1))+ans[pre];
for(int i=head[a]; i; i=e[i].next) {
if(e[i].v==pre) continue;
dfs3(sum,e[i].v,a);
}
}
inline void solve2() {
ll sum=0;
for(int i=1; i<=n; i++) v[i]=c[i],sum+=c[i];
dep[0]=-1;
dfs2(1,0);
for(int i=2; i<=n; i++) ans[1]+=c[i]*dep[i];
for(int i=head[1]; i; i=e[i].next) dfs3(sum,e[i].v,1);
}
int main() {
int a,b;
T=read();
while(T--) {
edgenum=0;
memset(ans,0,sizeof ans);
memset(head,0,sizeof head);
n=read();
for(int i=1; iread(),b=read();
addedge(a,b),addedge(b,a);
}
t=read();
for(int i=1; i<=n; i++) c[i]=read();
if(t) solve1();
else solve2();
for(int i=1; i<=n; i++) printf("%lld ",ans[i]);
putchar('\n');
}
return 0;
}
题解:
(见我另一篇博客:
http://blog.csdn.net/Demon_Rieman/article/details/78066943)
题解:
(好高级的做法) 明显我们要用 dp ,一开始想的二维 dp ,后来看存不下。所以这题只用一个一维 dp 就可以了。先说一下二维的, dp[i][j] 表示选 j 次,下余 i 的期望, p[i] 表示只选一次,选到i的期望,然后转移很明显,就是 dp[i+1][j]=(∑0≤m<moddp[i][m]∗p[m])mod1000000007 ,这样时间复杂度是 O(mod2logm) 的,空间复杂度是 O(mod) 的
都不行,于是我们观察,发现每次都要用 来转移,并且每次转移方式都是一样的,那不就是???对啦,递归的,也就是说,实际上答案就是的次方(类似矩阵),然后就写一个伪.矩阵快速幂就可以了,这样空间,时间,可以过。(也有人写滚动数组,但我不熟啊,所以就写了一个不滚动的。但是滚动要快一倍……)
代码:
#include
#include
using namespace std;
typedef long long ll;
const ll size = 1005;
const ll MOD = 1000000007;
ll n , m, mode;
ll ans[size] , replace[size] ,t[size];
ll q_pow(ll a,ll b,ll c)
{
ll res = 1;
while (b)
{
if (b%2) (res*=a)%=c;
b/=2;
(a*=a)%=c;
}
res %= c;
return res;
}
ll inv(ll a,ll b)//b 是质数
{
return q_pow(a , b - 2 ,MOD);
}
void mat_mult(ll *a , ll b[])
{
ll tmp[size];memset(tmp,0,sizeof tmp);
for (ll i = 1;i < mode;i++)
for (ll j = 1;j < mode;j++)
( tmp[ (i * j) % mode ] += a[i] * b[j] )%= MOD;
for (ll i = 1;i < mode;i++)
a[i] = tmp[i];
}
void mat_q_pow(ll *a,ll *b,ll m)//答案 mod c
{
a[1] = 1;
while (m)
{
if (m%2==1) mat_mult(a,b);
m/=2;
mat_mult(b,b);
}
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&mode);
for (int i = 1;i<=n;i++)
{
ll x;
scanf("%lld",&x);
t[x]++;
}
ll invn = inv( n , MOD);
for (int i = 1;i < mode;i++)
replace[i] = (t[i] * invn)%MOD ;
//for (int i = 0;i
//printf("%lld " , replace[i]);
mat_q_pow(ans , replace , m );
ll tot = 0;
for (ll i= 1;i < mode;i++)
tot = (tot + i * ans[i])%MOD;
(tot += MOD)%=MOD;
printf("%lld",tot);
}
- T2 便
题解:
限制条件推一推就变成了对于每一行,其增量相同,也就是对于同一行的 mat[k][i]−mat[k][j] 对于每一个 k 都相等(当然列也如此),所以我们只需做一个带权并查集就可以了。如何保证不是负数?只需要每次接父节点时都接小的,然后看这个加上增量是不是负的就行了。
代码:
#include
typedef long long ll;
const ll size =200201;
using namespace std;
int rank[size], twoo[size], mi[size], fa[size],_r, _c, n,T;
struct ob {
int ri, ci, rank;
bool operator <(const ob &a) const
{ return ci < a.ci; }
}obb[size];
int findfa(int x) {
if (fa[x] == x) return x;
int rt = findfa(fa[x]);
rank[x] += rank[fa[x]];
fa[x] = rt;
return fa[x];
}
int unionm(int a,int b,int ci)
{
int faa = findfa(a),fab = findfa(b);
if (rank[faa]>rank[fab]) fa[faa] = fab;
if (rank[faa] == rank[fab]) rank[faa]++;
}
bool merge(int a, int b, int ci)
{
int ra = findfa(a), rb = findfa(b);
if (ra != rb)
{
fa[ra] = rb;rank[ra] = ci - rank[a] + rank[b];
return 1;
}
else
{
bool yk= (rank[a] == rank[b] + ci);
return yk;
}
}
bool panding() {
for(int i= 0;i<= n;i++)
{
fa[i] = i;
rank[i] = 0;
}
sort(obb + 1, obb + 1 + n);
for(int i= 1;i<= n - 1;i++) if(obb[i].ci == obb[i + 1].ci)
if(!merge(obb[i].ri, obb[i + 1].ri, obb[i + 1].rank - obb[i].rank)) return false;
memset(twoo, 0x3f, sizeof(twoo));memset(mi, 0x3f, sizeof(mi));
for(int i= 1;i<= n;i++)
{
int rt = findfa(obb[i].ri);
twoo[rt] = min(twoo[rt], obb[i].rank + rank[obb[i].ri]);
}
for(int i=0;i<=_r;i++)
{
int rt = findfa(i);
mi[rt] = min(mi[rt], -rank[i]);
}
for(int i=0;i<= _r;i++)
if (fa[i] == i && (twoo[i] + mi[i] < 0))
return 0;
return 1;
}
int check = 1;
int main(){
scanf("%d",&T);
while(T--)
{
check = 1;
scanf("%d%d%d",&_r,&_c,&n);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&obb[i].ri,&obb[i].ci,&obb[i].rank);
for(int i= 0;i<= n;i++)
if (obb[i].rank < 0) check = 0;
if (panding()&&check)
printf( "Yes\n");
else printf("No\n");
}
}
return 0;
}
- T3 做
(惊天好题,这么好的题目背景,肯定要截下来)
题解:
实际是水题,膜法师肯定不会动,所以他只会膜或者喊:“苟利国家生死以,竹外桃花三两枝。”恢复,而香港记者则每次使 abs(x记者−x你) 与 abs(y记者−y你) 中较大的减少,因为要使平方和最小。模拟一遍就行了。
代码:
#include
#include
#include
using namespace std;
int T;
int main()
{
scanf("%d",&T);
int a,b;
scanf("%d%d",&a,&b);
while (T--)
{
int x1,x2,y1,y2;
int c ,d;
scanf("%d%d%d%d%d%d" , &x1 , &y1 , &x2 , &y2 , &c , &d);
int dx = abs(x1-x2) , dy = abs(y1-y2);
int tot = 0;
while (dx > 0 || dy > 0)
{
if (c < a) c+=b;
else {tot += dx*dx + dy*dy; c -= a;}
if (tot >= d) break;
if (dx > dy) dx--;else dy--;
if (dx > dy) dx--;else dy--;
}
if (tot>=d) printf("NAIVE\n");
else printf("SIMPLE\n");
}
return 0;
}
T1 数组开小了,气啊。下次一定要好好注意啊,考试时可不能犯这种错误。
T2 考试时没怎么想,连30的暴力都没写,主要是对树的题还是不是很熟。
T3 我推了好久啊,但也没推出正解,知识水平还不够啊,组合数学要多学学,虽然数学我最不好的就是组合数学,最后写暴力dp还得了80分,所以应该好好调整做题时间,想很久想不出来先把暴力写了,然后把题做完再来看这道题。(这题想了2个小时,但暴力5多分钟时候就想到了,然后就这样浪费了1小时50分钟,如果拿来做第二题,还能得30分)。
T1 做不来,就只写了一个20分的骗分,结果这都只骗到10分。 如果多想想还是有可能想出来的。然后改的时候居然模数搞错了,害的只有20,改了一下午才发现…….
T2 跟day1的T3一样,花了我大多时间,一开始想正解,没想出来(以前基本没用过带权并查集),然后写90分的暴力,就是强行记录差以及强行把矩阵做出来,但不知为何只有10,码力不足啊,一定是哪写错了。
T3 千古奇冤啊,我写平方时,居然写成了数学的,没想到样例还能过,然后这题又简单,就没去造数据了。这题告诉我再简单的题也要造点数据验证。
很重要的:考试时一定要注意数组大小,符号,模数。