【NOIP2018模擬賽2018.10.26】
第一題數論,證不出來,,,
spfa統計到每個點最短路數量,乘起來就是答案。
統計方法:更新最短路就把此點最短路數量重新賦為1,遇到相等dis[x] + w == dis[to]的情況則數量++。
程式碼:
#include<bits/stdc++.h> using namespace std; #define ll long long #define pt putchar #define gc getchar #define ko pt(' ') #define ex pt('\n') const int MAXN = 1005; const int MAXM = 1e6 + 5; const ll MOD = 2147483647; int n,m; ll dis[MAXN],ans = 1; bool vis[MAXN]; struct edge { int next,to,w; }e[MAXM<<1]; int head[MAXM<<1],cnt = 0; void add(int u,int v,int w) { e[++cnt].next = head[u]; e[cnt].to = v; e[cnt].w = w; head[u] = cnt; e[++cnt].next = head[v]; e[cnt].to = u; e[cnt].w = w; head[v] = cnt; } int ing[MAXN]; void in(int &x) { int num = 0,f = 1; char ch = gc(); while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();} while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();} x = num*f; } void lin(ll &x) { ll num = 0,f = 1; char ch = gc(); while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();} while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();} x = num*f; } void out(ll x) { if(x < 0) x = -x,pt('-'); if(x > 9) out(x/10); pt(x % 10 + '0'); } int q[MAXN<<2]; void spfa() { memset(dis,0x3f,sizeof dis); memset(vis,0,sizeof vis); int h = 0,t = 0; q[++t] = 1; vis[1] = 1; dis[1] = 0; while(h < t) { int x = q[++h]; vis[x] = 0; for(int i = head[x];i;i = e[i].next) { int to = e[i].to,w = e[i].w; if(dis[to] > dis[x] + w) { dis[to] = dis[x] + w; ing[to] = 1; if(!vis[to]) vis[to] = 1,q[++t] = to; } else if(dis[to] == dis[x] + w)ing[to]++; } } } int main() { // freopen("tree.in","r",stdin); // freopen("tree.out","w",stdout); in(n); in(m); for(int i = 1;i <= m;i++) { int x,y,z; in(x),in(y),in(z); add(x,y,z); } spfa(); for(int i = 1;i <= n;i++) if(ing[i] > 1) ans = ans * ing[i] % MOD; out(ans); return 0; }
貪心 + multiset 二分 之所以用multiset是因為不同草的價格可能一樣,都得統計入答案選擇中。
先按fresh程度把牛和草從大到小排序,然後列舉每頭牛(按fresh程度從大到小),每次把fresh大於等於牛要求的草加入到multiset裡,然後在multiset直接二分查詢(lower_bound)大於等於牛要求的價格的草,、。
若找不到肯定無解,輸出-1;找到就將草價格加入答案,把找到草的資訊從multiset中刪除。
貪心可以證明正確性。我才不是懶得寫怎麼證確實是懶,不證了。
程式碼:(跑的挺快的?)
#include<bits/stdc++.h> using namespace std; #define ll long long #define ubr int #define pt putchar #define gc getchar #define ko pt(' ') #define ex pt('\n') const int MAXN = 1e6 + 5; int n,m; int maxfresh = 0,maxcost = 0; int fresh = 0,cost = 0; ll ans = 0; struct cow { int cost,fresh; bool operator < (const cow one) const { return fresh > one.fresh; } }c[MAXN<<1],cao[MAXN<<1]; //struct grass //{ // int cost,fresh; // bool operator < (const grass one) const // { // return fresh > one.fresh; // } //}cao[MAXN<<1]; void in(int &x) { int num = 0,f = 1; char ch = gc(); while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();} while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();} x = num*f; } void lin(ll &x) { ll num = 0,f = 1; char ch = gc(); while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();} while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();} x = num*f; } void out(ll x) { if(x < 0) x = -x,pt('-'); if(x > 9) out(x/10); pt(x % 10 + '0'); } multiset<int> s; multiset<int> :: iterator it; int main() { in(n); in(m); if(m < n) {cout << -1; return 0;} for(int i = 1;i <= n;i++) { in(c[i].cost),in(c[i].fresh); maxcost = max(maxcost,c[i].cost); maxfresh = max(maxfresh,c[i].fresh); } for(int i = 1;i <= m;i++) { in(cao[i].cost),in(cao[i].fresh); cost = max(cost,cao[i].cost); fresh = max(fresh,cao[i].fresh); } if(maxcost > cost || maxfresh > fresh) {cout << -1; return 0;} sort(c+1,c+1+n); sort(cao+1,cao+1+m); int j = 1; for(int i = 1;i <= n;i++) { while(cao[j].fresh >= c[i].fresh && j <= m) s.insert(cao[j++].cost); it = s.lower_bound(c[i].cost); if(it == s.end()) {out(-1); return 0;} ans += *it; s.erase(it); } out(ans); return 0; }
嘛這道題剛剛看到會迷茫是當然的,因為其演算法是亂搞出來的。(也許是???),題目又扯聯通塊又扯樹操作,資料範圍明顯隨性,自然想到應該亂搞。
具體來說,這道題基本思路就是列舉每個點作為其可以在的聯通塊裡的最大值點進行dfs,遇見比它值小的點且兩者之差不大於k就繼續dfs下去,直到不滿足上面的條件。
方案數統計方法看程式碼,乘法原理不想多說。
具體需要注意的細節在於取模和判重。
1、取模注意步步取模(不解釋了吧dalao們因為這個一路丟了多少分不再累述)。
2、判重在於如果有兩個點點權一樣大,那麼它們互相會搜到對方的情況(當然前提是能搜到對方)是會重複統計的,我們想避免的話該怎麼辦呢?
人為規定,當點權相等時,只能從編號小節點搜到編號大的節點,這樣就不會重複統計了。(具體看程式碼dfs中最後倆個判斷)
程式碼:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt putchar
#define gc getchar
#define ko pt(' ')
#define ex pt('\n')
const int MAXN = 1e4 + 5;
const int MAXM = 1e4 + 5;
const ll MOD = 19260817;
int n; ll k;
ll dis[MAXN],ans1 = 0,ans2 = 0;
ll w[MAXN];
bool vis[MAXN];
struct edge
{
int next,to;
}e[MAXM<<1];
int head[MAXM<<1],cnt = 0;
void add(int u,int v)
{
e[++cnt].next = head[u]; e[cnt].to = v; head[u] = cnt;
e[++cnt].next = head[v]; e[cnt].to = u; head[v] = cnt;
}
int ing[MAXN];
void in(int &x)
{
int num = 0,f = 1; char ch = gc();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
x = num*f;
}
void lin(ll &x)
{
ll num = 0,f = 1; char ch = gc();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
x = num*f;
}
void out(ll x)
{
if(x < 0) x = -x,pt('-');
if(x > 9) out(x/10);
pt(x % 10 + '0');
}
ll dfs(int x,int fr,int Max)
{
ll now = 1;
for(int i = head[x];i;i = e[i].next)
{
int to = e[i].to;
if(to != fr && w[Max] >= w[to] && w[Max] - w[to] <= k
&& (w[Max] != w[to] || Max < to))
now = (now * (dfs(to,x,Max) + 1)) % MOD;
}
return now % MOD;
}
int main()
{
in(n); lin(k);
for(int i = 1;i <= n;i++) lin(w[i]);
for(int i = 1;i < n;i++)
{
int x,y; in(x),in(y);
add(x,y);
}
for(int i = 1;i <= n;i++)
ans1 = (ans1 + dfs(i,0,i) % MOD) % MOD;
if(k){
k--;
for(int i = 1;i <= n;i++)
ans2 = (ans2 + dfs(i,0,i) % MOD) % MOD;
}
out((ans1 - ans2 + MOD) % MOD);
return 0;
}
ps:被同學說程式碼風格新奇,但是我覺得很普通啊,若哪位大佬也覺得醜的一批請告訴我,避免蒟蒻越走越偏。。