B.數字分佈 (構造)
題目描述
https://nanti.jisuanke.com/t/45507
構造一個含有n個正整數的序列,並滿足以下要求:
1.序列是不遞減的,a1 <= a2 <= … <= an
2.序列中滿足 j<i 且 ai mod aj = 0 的對數爲m
3.序列中所有數字不大於2⋅n
Solution
首先我們考慮1:
1可以和任何數組成一對。所以對於當前位置放置1,可以組成( n - i )對。
再考慮非1的情況:
爲了防止它們成對,我們對這些位置填的數在(n + 1)到 2 * n 之間。
這樣它們就被分成一個個連續相同數字的塊,組對只能是在相同的塊裏組,假設當前塊有k個,組成的對有k *(k - 1)/ 2對.
按上述方法構造即可。
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int SZ = 2e5 + 20;
int n;
vector<int>num;
ll m;
int main()
{
int T;
scanf("%d",&T);
while(T --)
{
scanf("%d%lld",&n,&m);
num.clear();
int i = 1;
ll w = n;
while(i <= n && m - n + i >= 0)
{
m -= n - i;
i ++;
printf("1 ");
}
while(w && m)
{
while(w * (w - 1) / 2 <= m)
{
num.push_back(w);
m -= (w - 1) * w / 2;
}
w --;
}
w = n + 1;
int j = 0;
for(;i <= n;i ++)
{
printf("%d ",w);
if(j < num.size())
{
num[j] --;
if(num[j] == 0)
{
j ++;
w ++;
}
}
else
w++;
}
printf("\n");
}
return 0;
}
D.旅遊(倍增)
https://blog.csdn.net/qq_45620330/article/details/106647957
G.選根(dp)
和這題非常像
https://www.luogu.com.cn/problem/P3478
題目描述
https://nanti.jisuanke.com/t/45572
有一顆有n個結點樹,結點被編號爲1~n,記根結點深度爲1,如果第i個結點的深度是d,則它貢獻的價值是d×wi,這棵樹的價值是所有結點的價值和。
求當根結點爲1~n時,樹的價值分別爲多少?
Solution
換根dp經典題
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int SZ = 2e6 + 20;
int n,temp;
ll dp[SZ],dep[SZ];
int fist[SZ];
ll w[SZ],ans[SZ],sumw[SZ];
struct zt
{
int v,nxt;
}line[SZ];
inline void build(int x,int y)
{
line[++ temp] = (zt){y,fist[x]};
fist[x] = temp;
}
inline void dfs1(int u,int far)
{
sumw[u] = w[u];
dp[u] = dep[u] * w[u];
for(int i = fist[u];i != -1;i = line[i].nxt)
{
int v = line[i].v;
if(v == far) continue;
dep[v] = dep[u] + 1;
dfs1(v,u);
sumw[u] += sumw[v];
dp[u] += dp[v];
}
}
inline void dfs2(int u,int far)
{
ans[u] = dp[u];
for(int i = fist[u];i != -1;i = line[i].nxt)
{
int v = line[i].v;
if(v == far) continue;
dp[v] = dp[u] + sumw[u] - 2 * sumw[v];
sumw[u] -= sumw[v];
sumw[v] += sumw[u];
dfs2(v,u);
sumw[v] -= sumw[u];
sumw[u] += sumw[v];
}
}
int main()
{
int a,b;
int T;
scanf("%d",&T);
while(T --)
{
temp = 0;
memset(fist,-1,sizeof(fist));
scanf("%d",&n);
for(int i = 1;i <= n;i ++) scanf("%lld",&w[i]);
for(int i = 1;i < n;i ++)
{
scanf("%d%d",&a,&b);
build(a,b);
build(b,a);
}
dep[1] = 1;
dfs1(1,1);
dfs2(1,1);
for(int i = 1;i <= n;i ++)
printf("%lld ",ans[i]);
printf("\n");
}
return 0;
}
H.排序
題目描述
https://nanti.jisuanke.com/t/45513
給你兩個長度均爲n的數列ai和bi定義函數
f(l,r)= ∑ai×bi (l <= i <= r)
g(x)= ∑∑f(l,r) (1 <= l <= x) (1 <= r <= x)
你可以改變ai內各元素的順序,使g(n)最小,求這個最小值。由於答案很大,請輸出答案對10^9+7 取餘後的值.
Solution
因爲bi不可以換序,所以對於每個bi來說,它要被計算i * (n - i + 1)次,
ai可以換序,所以只要ai中大的數與bi * i * (n - i + 1)中小的數相乘,使得乘積和最小即可,具體操作只要把ai 和 bi * i * (n - i + 1)按照反方向排序,對應相乘即可。
注意取模,別炸了LL。
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//__builtin_popcount(n);
//-std=c++11
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define RI register int
const int MOD = 1e9 + 7;
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int SZ = 2e5 + 10;
int n;
ll a[SZ],b[SZ];
bool cmp(ll x,ll y)
{
return x > y;
}
ll c[SZ];
int main()
{
int T;
scanf("%d",&T);
while(T --)
{
ll ans = 0;
scanf("%d",&n);
for(int i = 1;i <= n;i ++) scanf("%lld",&a[i]);
for(int i = 1;i <= n;i ++) scanf("%lld",&b[i]);
for(int i = 1;i <= n;i ++) b[i] = b[i] * i * (n - i + 1);
sort(a + 1,a + n + 1,cmp);
sort(b + 1,b + n + 1);
for(int i = 1;i <= n;i ++)
{
c[i] = (a[i] * (b[i] % MOD)) % MOD;
ans = (ans + c[i]) % MOD;
}
printf("%lld\n",ans);
}
return 0;
}
I.支付(構造)
https://nanti.jisuanke.com/t/45487
題目描述
有n個物品,第ii個物品的價值爲wi,選擇若干物品,這些物品的價值和爲S
作爲支付手段,希望選擇一部分物品能精確地支付不大於k的所有金額,即任意s滿足1≤s≤k都有對應的選擇方案使S=s,給定k的值,構造滿足要求的序列wi,使n儘可能小的前提下wi的和儘可能小。
Solution
構造一個20,21,22,…,2t 和 k - Σ2i的序列爲答案。
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll k;
int main()
{
int T;
scanf("%d",&T);
while(T --)
{
scanf("%lld",&k);
ll temp = k,cnt = 0;
while(temp){cnt ++;temp >>= 1;}
ll sum = pow(2,cnt - 1);
k -= (sum - 1);
ll t = 1;
printf("%lld\n",cnt);
for(int i = 1;i <= cnt - 1;i ++)
{
printf("%lld ",t);
t <<= 1;
}
printf("%lld\n",k);
}
return 0;
}
J.球衣
https://nanti.jisuanke.com/t/45578
題目描述
共有n支隊伍參加足球聯賽,每支隊伍有3件球衣編號爲1∼3,每件球衣都有顏色.球衣顏色按RGB格式給出,RGB顏色表示分爲3部分,分別是紅色綠色藍色的亮度。每種顏色的亮度在0∼255之間,用十六進制表示,如紅色亮度爲255,綠色亮度爲120,藍色亮度爲160就組成了粉色,RGB十六進制表示爲#FF78A0,每種顏色的亮度佔2位
在比賽時,爲了避免混淆,兩隊的球衣顏色不能過於相似,要求兩隊球衣的顏色差不小於128,顏色差是兩隊球衣顏色紅綠藍亮度差的絕對值之和,#FF78A0 rgb(255, 120, 160)和#78A0FF rgb(120,160,255)的顏色差是∣255−120∣+∣120−160∣+∣160−255∣=270
在A隊主場對陣B隊的比賽中,A隊首先選擇1號球衣,B隊依次選擇1∼3號球衣,如果都不能滿足要求,A隊選擇2號球衣,B隊依次選擇1∼3號球衣,如果還不滿足,A隊選擇3號球衣,B隊依次選擇1∼3號球衣
也就是說兩隊都希望選擇編號儘可能小的球衣,但是主場作戰的隊伍優先選擇,如果主場隊伍編號小的球衣與客場隊伍的三件球衣都相似,纔會選擇編號大的球衣.
比賽採取主客場雙循環賽制,請你計算每次比賽兩隊的球衣顏色
Solution
十六進制讀入,按要求模擬即可。
代碼
#include <bits/stdc++.h>
using namespace std;
const int SZ = 100 + 20;
int n;
int color[SZ][5];
int contest[2][SZ][SZ];
int dif(int x,int y)
{
int sum = 0;
for(int i = 0;i < 3;i ++)
{
sum += abs(x % 256 - y % 256);
x /= 256;
y /= 256;
}
return sum;
}
int main()
{
int T;
scanf("%d",&T);
while(T --)
{
memset(contest,0,sizeof(contest));
scanf("%d",&n);
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= 3;j ++)
scanf("%X",&color[i][j]);
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= n;j ++)
{
if(i == j) continue;
for(int t = 3;t >= 1;t --)
for(int p = 3;p >= 1;p --)
if(dif(color[i][t],color[j][p]) >= 128)
{
contest[0][i][j] = t;
contest[1][j][i] = p;
}
}
for(int k = 0;k <= 1;k ++)
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= n;j ++)
printf("%d%c",contest[k][i][j],j == n?'\n':' ');
}
return 0;
}
M.再來異或
https://nanti.jisuanke.com/t/45576
題目描述
給你具有n個結點n−1條邊的無向無環連通圖,結點編號1∼n,每條邊上有一個數作爲他的邊權,定義函數f(i,j)爲連接i,j的簡單路徑的所有邊權的異或值
求⨁⨁f(i,j) (1 <= i <= n) (i <= j <= n)
⊕爲按位異或運算,⨁i ( l <= i <= r)表示l∼r所有整數異或後的結果
Solution
求樹中所有結點兩兩組合的函數值的異或和,只需要考慮每條邊被計算了多少次,偶數次則爲0,奇數次則對答案有貢獻。只要判斷當前邊兩側節點個數的乘積爲奇數還是偶數即可判斷當前邊被計算的奇偶次。
代碼
#include <bits/stdc++.h>
using namespace std;
const int SZ = 2e5 + 10;
typedef long long ll;
int ans,n;
struct zt
{
int v,w,nxt;
}line[SZ << 1];
int temp,fist[SZ];
inline void build(int x,int y,int z)
{
line[++ temp] = (zt){y,z,fist[x]};
fist[x] = temp;
}
inline int dfs(int u,int far)
{
int cnt = 1;
for(int i = fist[u];i != -1;i = line[i].nxt)
{
int v = line[i].v;
if(v == far) continue;
int temp = dfs(v,u);
if(temp % 2 * (n - temp) % 2 == 1)
ans ^= line[i].w;
cnt += temp;
}
return cnt;
}
int main()
{
int T;
scanf("%d",&T);
while(T --)
{
temp = 0;
memset(fist,-1,sizeof(fist));
scanf("%d",&n);
int a,b,c;
for(int i = 1;i <= n - 1;i ++)
{
scanf("%d%d%d",&a,&b,&c);
build(a,b,c);
build(b,a,c);
}
ans = 0;
dfs(1,1);
printf("%d\n",ans);
}
return 0;
}
2020.6.9