D1 T1:Clique:
我做的题太少啦,这都没看出来。首先,这个式子是 c[i]−c[j]>=dis(i,j) ,即在数轴上这样的圆,如果没交点,那么就有边,所以就是最长区间不覆盖。
#include
#include
using namespace std;
const int size = 200005;
struct seg{
long long l, r;
inline bool operator <(const seg &fu)const
{if (fu.r==r) return l < fu.l;
return r < fu.r; }
}a[size];
long long n;
int main()
{
scanf("%lld",&n);
for (long long i=1;i<=n;i++)
{
long long tv,tw;
scanf("%lld%lld",&tv,&tw);
a[i].l = tv-tw;a[i].r = tv + tw;
}
sort(a+1,a+1+n);
long long r = a[1].r;
long long ans = 1;
for (long long i = 2 ; i <= n ; i++)
{
if (a[i].l >= r)
{
ans++;
r = a[i].r;
}
}
printf("%lld",ans);
return 0;
}
D1 T2:Mod
好题啊!!要记下来。
套路题。首先,如果一个区间内最大的数都没有模数大,就不用模啦,因为这是无效的膜。又每次取模后,都只最多剩下以前的一半,所以在这个数不变的情况下,这个数最多有效的被模 logn 次,这个可以暴力修改。那么每个数被修改 logn 次,一次改是 logn 的,总的就是 nlognlogn 。
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int size = 100005;
struct NODE{
ll maxn,cnt;
}t[4*size];
ll n,m;
void update1(ll l,ll r,ll qx,ll y,ll node)
{
if (l==r)
{
t[node].maxn = y;
t[node].cnt = y;
//printf("fuck %d %d\n",y,t[l].cnt);
return ;
}
ll mid=(l+r)>>1;
if (qx <= mid) update1(l,mid,qx,y,node*2);
if (qx >= mid+1) update1(mid+1,r,qx,y,node*2 + 1);
t[node].maxn = max( t[node * 2].maxn , t[node*2+1].maxn ) ;
t[node].cnt = t[node*2].cnt + t[node*2+1].cnt ;
//printf("node %d l %d r %d cnt %d maxn %d mid %d\n",node,l,r,t[node].cnt,t[node].maxn,mid);
}
void build(ll node,ll l,ll r)
{
if (l==r)
{
t[node].cnt=0;
t[node].maxn=0;
return ;
}
ll mid = (l+r)>>1;
build(node*2,l,mid);
build(node*2+1,mid+1,r);
}
void update2(ll ql,ll qr,ll l,ll r,ll x,ll node)
{
if (r < ql || l > qr) return;
if (t[node].maxn < x) return;
if (l==r)
{
t[node].cnt%=x;
t[node].maxn%=x;
return;
}
ll mid = (l+r)>>1;
update2(ql,qr,l,mid,x,2*node);
update2(ql,qr,mid+1,r,x,2*node+1);
t[node].maxn = max(t[2*node].maxn , t[2*node+1].maxn);
t[node].cnt = t[2*node].cnt + t[node*2+1].cnt;
}
ll query(ll l,ll r,ll ql,ll qr,ll node)
{
if (r < ql || l > qr) return 0;
ll ans = 0;
if (ql <= l && r <= qr) return t[node].cnt;
ll mid = l+r>>1;
ans += query(l,mid,ql,qr,2*node) + query(mid+1,r,ql,qr,2*node+1);
return ans;
}
void dfs(ll l,ll r,ll node)
{
printf("l is %d r is %d node is %d cnt is %d \n",l,r,node,t[node].cnt);
if (l==r) return ;
ll mid=l+r>>1;
dfs(l,mid,node*2);
dfs(mid+1,r,node*2+1);
}
int main()
{
//freopen("mod.in","r",stdin);
//freopen("modfuck.out","w",stdout);
scanf("%lld%lld",&n,&m);
ll dod;
build(1,1,n);
for (ll i=1;i<=n;i++)
{
ll tmp;scanf("%lld",&tmp);
update1(1,n,i,tmp,1);
}
for (ll i=1;i<=m;i++)
{
scanf("%lld",&dod);
if (dod==1)
{
ll l,r;scanf("%lld%lld",&l,&r);
ll ans = query(1,n,l,r,1);
printf("%lld\n",ans);
}else
if (dod==2)
{
ll l,r,x;
scanf("%lld%lld%lld",&l,&r,&x);
update2(l,r,1,n,x,1);
//dfs(1,n,1);
}else
if (dod==3)
{
ll k,y;
scanf("%lld%lld",&k,&y);
update1(1,n,k,y,1);
}
}
return 0;
}
D1 T3:Number
就是你会发现,随着n的变大,那段区间在二进制中有 k 个一的数不会减少。因为从 k 到 k+1 时,有 2k 在二进制中与 k 的 1 的个数相同,所以不会减少。那么就可以数位dp一下。令 S(i)=i+1,2,…,2i , f(i,k) 表示 S(i) 中二进制下恰好有 k 个 1 的数的个数 (i>0,k≥0) 。设为中有几个数 f(i,k)=Σmin(k,p)x=1Ck−x+1ax+1−Ck−x+1ax ,其中 p 表示 i 在二进制下1的个数, ax 表示 i在二进制下第x高的1所在位代表的2的幂次。特判掉 k≤1 ,那么所有满足条件的 n都在 2∗1018 以内。 用二分或逐位贪心求出最小和大的 n 即可。
#include
#include
#define MAX_DIG 63
using namespace std;
typedef unsigned long long lnt;
int k; lnt m, c[MAX_DIG+5][MAX_DIG+5], pow[MAX_DIG+5] = {1}, range = 2e18;
void init() {
for (int i = 1; i <= MAX_DIG; i++) pow[i] = pow[i-1]*2;
for (int i = 0; i <= MAX_DIG+2; i++) c[i][0] = 1, c[i][i] = 1;
for (int i = 1; i <= MAX_DIG+2; i++) for (int j = 1; j <= i; j++) c[i][j] = c[i-1][j-1]+c[i-1][j];
}
lnt f(lnt tmp) {
int p = 0; lnt ret = 0;
for (int i = 0; i <= MAX_DIG; i++) if (tmp&pow[i]) p++;
for (int x = 1, cur = MAX_DIG; x <= min(p, k); x++, cur--) {
while (!(pow[cur]&tmp)) cur--;
ret += c[cur+1][k-x+1]-c[cur][k-x+1];
}
return ret;
}
lnt fl(lnt l, lnt r) {
lnt ret = -1;
while (l <= r) {
lnt mid = l+r>>1;
if (f(mid) <= m) ret = mid, l = mid+1;
else r = mid-1;
}
return ret;
}
lnt fu(lnt l, lnt r) {
lnt ret = -1;
while (l <= r) {
lnt mid = l+r>>1;
if (f(mid) >= m) ret = mid, r = mid-1;
else l = mid+1;
}
return ret;
}
int main() {
int T; scanf("%d", &T), init();
while (T--) {
scanf("%llu%d", &m, &k); if (m == 1 && k == 1) {printf("-1\n"); continue;}
if (m == 0) {printf("1 %llu\n", pow[k-1]-1); continue;}
lnt lo =fu(1, range), hi = fl(1, range);
printf("%llu %llu\n", lo, hi);
}int sdf=0;
for (int i=1;i<=10000000;i++) sdf+=0;
return 0;
}
D2 T1 :Mine
神奇的定义与转移。设 f(i,j) 表示当前填第 i 个字符为 j 的方案数,即j表示当前这个位置还缺几个雷,如果是2就表示这个位置本身就是雷。然后 dp 就好搞啦。
#include
#include
#include
using namespace std;
typedef long long ll;
const int size = 1000005;
const ll mod = 1000000007;
char s[size];
int dp[size][10],len;
int main(){
scanf("%s", s);
len = strlen(s);
if(len == 1){
if(s[0] == '?')printf("2");
if(s[0] == '1' || s[0] == '2')printf("0");
return 0;
}
if(s[0] == '0')dp[0][0] = 1;
if(s[0] == '1')dp[0][1] = 1;
if(s[0] == '2'){printf("0");return 0;}
if(s[0] == '*')dp[0][2] = 1;
if(s[0] == '?')dp[0][0] = dp[0][1] = dp[0][2] = 1;
for(int i = 1;i < len;i ++ )
{
if(s[i] == '0') dp[i][0] = dp[i-1][0]; else
if(s[i] == '1')
{
if(i < len-1)dp[i][1] = dp[i-1][0];
dp[i][0] = dp[i-1][2];
}else
if(s[i] == '2')dp[i][1] = dp[i-1][2]; else
if(s[i] == '*')dp[i][2] = (dp[i-1][1] + dp[i-1][2]) % mod; else
if(s[i] == '?')
{
if(i < len-1) dp[i][1] = (dp[i-1][0] + dp[i-1][2]) % mod;
dp[i][2] = (dp[i-1][1] + dp[i-1][2]) % mod;
dp[i][0] = (dp[i-1][0] + dp[i-1][2]) % mod;
}
}
ll ans = dp[len-1][0] + dp[len-1][2];
printf("%lld\n", ans);
return 0;
}
D2 T2:Water
就是水如果能流出去,那么一定有一条路径使得其路径上的值都小于其高度。如果流不出去,那么所有路径上的最大值的最小值要大于其高度(因为一条路径上水只会被最高的挡住,而木桶原理,水只会在最低的被挡住的高度被留住)。所以问题就是从这个块走出矩形的所有路径上的最大值的最小值。相邻块连边,权值为两块的较大值,矩形边界的块向“矩形外”连边,权值为 max(高度,0) ,做最小生成树。(最短路也可以)
#include
#include
#include
#include
#define inf 1<<30
typedef long long ll;
const ll size = 500;
using namespace std;
struct EDGE{
ll to,next,val;
inline bool operator < (const EDGE &du )const
{
return val < du.val;
}
}edge[100*size*size];ll cnt=0;
ll n,m,value;
ll mat[size][size] , dis[size*size], head[size*size];
bool in[size*size];
ll SPFA(ll node)
{
for (ll i = 0 ; i <= n*m ;i++)dis[i] = inf;
queue q;
q.push(node);dis[node]= 0;
memset(in,false,sizeof in);
in[node] = true;
while(!q.empty())
{
ll h = q.front();
for (ll j=head[h] ; j ;j= edge[j].next)
{
if (dis[edge[j].to ] > max(dis[h],edge[j].val) )
{
dis[ edge[j].to ] = max(dis[h] , edge[j].val);
if (!in[edge[j].to])
{
q.push(edge[j].to);
in[edge[j].to] = 1;
}
}
}
q.pop();in[h] = false;
}
}
ll idx(ll i,ll j)
{
return (i*m + j-m);
}
void adde(ll fr,ll to,ll val)
{
edge[++cnt]= (EDGE){to,head[fr],val};
head[fr] = cnt;
}
void addedge(ll fr,ll to,ll val)
{
adde(fr,to,val);
adde(to,fr,val);
}
int main()
{
//freopen("water.in","r",stdin);
//freopen("water.out","w",stdout);
scanf("%lld%lld",&n,&m);
for (ll i=1;i<=n;i++)
for (ll j=1;j<=m;j++)
scanf("%lld",&mat[i][j]);
for (ll i=1;i<=n;i++)
for (ll j=1;j1]);
addedge(idx(i,j),idx(i,j+1),value);
}
for (ll i=1;ifor (ll j=1;j<=m;j++)
{
value = max(mat[i][j] , mat[i+1][j]);
addedge(idx(i,j),idx(i+1,j),value);
}
for (ll i=2;i<=n-1;i++)
{
addedge(0 , idx(i,1) , max(mat[i][1] , (ll)0 ) );
addedge(0 , idx(i,m) , max(mat[i][m] , (ll)0 ) );
}
for (ll j=1;j<=m;j++) addedge(0,j,max((ll)0,mat[1][j]));
for (ll j=1;j<=m;j++) addedge(0 , idx(n,j) , max((ll)0,mat[n][j]));
SPFA(0);
for (ll i=1;i<=n;i++)
{
for (ll j=1;j<=m;j++)
{
if (dis[idx(i,j)] <= mat[i][j]) printf("0 ");
else printf("%lld ",dis[idx(i,j)]-mat[i][j]);
//printf("%lld ",dis[idx(i,j)]);
}
putchar('\n');
}
//for (ll i=1;i<=m*n;i++)
//printf("%d ",dis[i]);
return 0;
}
D2 T3:Gcd:
乱反演一下,然后那个桶记因数出现次数。用 f(i) 表示gcd为i的数对个数, g(i) 表示gcd为i的倍数的数对个数。那么 f(i)=Σμ(d)g(d) ,我们只需要维护 g(1) g(max(xi)) 。记s(i)表示i的倍数的个数,那么 g(i)=s(i)∗(s(i)−1)/2 ,我们只需要在加入/删除一个数时枚举它的因数修改s即可。
#include
#include
#include
using namespace std;
typedef long long ll;
const int size = 500005;
bool is_prime[size];
int prime[size],cnt,ud[size];
int s[size] , in[size] ,a[size];
int n,m,maxn=size;
ll ans=0;
void doyoudo()
{
memset(is_prime,true,sizeof is_prime);
is_prime[1] = 1;ud[1] = 1;
for (int i=2;i<=maxn;i++)
{
if (is_prime[i])
{
prime[++cnt] = i;
ud[i] = -1;
}
for (int j=1;j<=cnt;j++)
{
int tmp = i*prime[j];
if (tmp>maxn) break;
is_prime[tmp] = false;
if (i % prime[j]==0)
{
ud[tmp] = 0;
break;
}
else
ud[tmp] = ud[i] * (-1);
}
}
}
void add(int x)
{
for (int i=1;i*i<=x;i++)
{
if (x%i) continue;
ans += ud[i] * (s[i]++);
if (i*i!=x)
ans += ud[x/i] * (s[x/i]++);
}
}
void delet(int x)
{
for (int i=1;i*i<=x;i++)
{
if (x%i) continue;
ans -= ud[i] * (--s[i]);
if (i*i!=x)
ans -= ud[x/i] * (--s[x/i]);
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
doyoudo();
memset(in,0,sizeof in);
while (m--)
{
int tmp;
scanf("%d",&tmp);
if (in[tmp]==1) delet(a[tmp]);
else add(a[tmp]);
in[tmp]^=1;
printf("%lld\n",ans);
}
//for (int i=1;i<=maxn;i++)
//printf("%d %d %d\n",i,is_prime[i],in[i]);
}
D3 T1:string
就是对每种字母单独造棵线段树,然后合并。有点神。
#include
#include
#include
#include
#include
#include
using namespace std;
int n,m,g[400010],p;
char s[100010];
struct str
{
int a[26];
inline void clear()
{
int i;
for(i=0;i<26;i++)
a[i]=0;
}
inline str operator+(str x)
{
str p;
int i;
for(i=0;i<26;i++)
p.a[i]=a[i]+x.a[i];
return p;
}
}f[400010],x;
inline void down(int i,int k)
{
if(g[i]!=-1)
{
g[i<<1]=g[i];
f[i<<1].clear();
f[i<<1].a[g[i]]=k;
g[i<<1|1]=g[i];
f[i<<1|1].clear();
f[i<<1|1].a[g[i]]=k;
g[i]=-1;
}
}
inline void change(int i,int j,int k,int l,int r,int x)
{
if(l<=j && k<=r)
{
f[i].clear();
f[i].a[x]=k-j+1;
g[i]=x;
}
else
{
down(i,k-j+1>>1);
if(l<=(j+k>>1))
change(i<<1,j,j+k>>1,l,r,x);
if(r>(j+k>>1))
change(i<<1|1,(j+k>>1)+1,k,l,r,x);
f[i]=f[i<<1]+f[i<<1|1];
}
}
inline str sum(int i,int j,int k,int l,int r)
{
if(l<=j && k<=r)
return f[i];
else
{
down(i,k-j+1>>1);
str p={};
if(l<=(j+k>>1))
p=p+sum(i<<1,j,j+k>>1,l,r);
if(r>(j+k>>1))
p=p+sum(i<<1|1,(j+k>>1)+1,k,l,r);
return p;
}
}
int main()
{
//freopen("string.in","r",stdin);
//freopen("string.out","w",stdout);
int i,j,k;
scanf("%d%d%s",&n,&m,&s);
for(p=1;p1);
for(i=1;i<2*p;i++)
g[i]=-1;
for(i=1;i<=n;i++)
change(1,1,p,i,i,s[i-1]-'a');
while(m--)
{
scanf("%d%d%d",&i,&j,&k);
x=sum(1,1,p,i,j);
if(k)
for(k=0;k<26;k++)
{
if(x.a[k])
change(1,1,p,i,i+x.a[k]-1,k);
i+=x.a[k];
}
else
for(k=25;k>=0;k--)
{
if(x.a[k])
change(1,1,p,i,i+x.a[k]-1,k);
i+=x.a[k];
}
}
for(i=1;i1);
for(i=1;i<=n;i++)
printf("%c",'a'+g[p+i-1]);
printf("\n");
return 0;
}
D3 T2:Matrix
这道题只好意会一下,用 f[i][j] 表示做到前 i列,已经有 j列在右侧区间放 1的方案数。 i 越过一个左侧区间的右端点时,从之前剩下空列中选一个在这个左侧区间放 1。也就是说,在扫到i时,我们只在右端点不大于i的左区间放1,转移时分在右侧区间放1或不放 1。还是好转移的。
#include
#include
#include
using namespace std;
typedef long long ll;
const ll size = 3003;
const ll mod = 998244353;
ll bucl[size] , bucr[size] , l[size] ,r[size];
ll p[size],inv[size];
ll dp[size][size];
ll n,m;
int main()
{
scanf("%lld%lld",&n,&m);
for (ll i=1;i<=n;i++)
{
scanf("%lld%lld",&l[i],&r[i]);
bucl[ l[i] ]++;
bucr[ r[i] ]++;
}
for (ll i=1;i<=m;i++){
bucl[i]+=bucl[i-1];
bucr[i]+=bucr[i-1];
}
//for (ll i=1;i<=m;i++)printf("%d ",bucl[i]);
//for (ll i=1;i<=m;i++)printf("%d ",bucr[i]);putchar('\n');
//inv[1]=1;p[1]=1;
//for (ll i=2;i<=n;i++) {p[i]= (p[i-1]*i)%mod; inv[i] = (mod-mod/i)*inv[i%mod];}
(dp[0][0]=1)%=mod;
for (ll i=1;i<=m;i++)
{
dp[i][0] = dp[i-1][0]%mod;
for (ll j = 1;j<= i;j++)
{
(dp[i][j] += ( dp[i-1][j-1]* (bucr[i] - j + 1 ) )%mod + dp[i-1][j])%=mod;
}
for (ll p = bucl[i-1]+1 ; p <= bucl[i] ; p++)
for (ll j = 0;j<=i;j++)
(dp[i][j] *= (i - p - j +1))%=mod;
}
/*for (ll i=1;i<=m;i++)
{
for (ll j=0;j<=m;j++)
printf("%d ",dp[i][j]);
printf("\n");
}*/
printf("%lld",dp[m][n]);
return 0;
}
D3 T3:Big
我们发现,对手是把我们现在得到的数在二进制下把第一位移到最右边(位数限制为 n 位的二进制数),又因为我们可以取遍1到 2n−1 的数,所以换不换没啥意义,就是要把a序列换一下。问题转化为选一个数,使它左移位后与 m+1 个给定数分别异或的最小值大。然后造字典树,如果当前为0,1都有,则出来只能是0,反之只有一个,则是1。
#include
#include
typedef long long ll;
const ll size = 100005;
int n,m;
int trie[32*size][2] , cnt = 0;
ll ans = 0 , anscnt = 0;
int a[size],b[size];
void insert (int x)
{
int now = 0;
for (int i = n ; i >= 1; i--)
{
int tmp = ( x & ( 1 << ( i - 1 ) ) ) > 0 ? 1 : 0;
if (!trie[now][ tmp ] ) trie[now][tmp] = ++cnt;
now = trie[now][tmp];
}
}
int Rieman_Lebel_Change(int x)//黎曼与勒贝尔变换(乱写的)
{
return ( ( 2 * x ) / ( 1 << n ) + (2 * x) ) % (1 << n );
}
void solve(int dep,int node,ll bigth)
{
if (dep <= 0)
{
if (bigth>ans)
{
ans = bigth;
anscnt = 1;
}
else if (bigth==ans) anscnt++;
return ;
}
if (trie[node][1]&&trie[node][0])
{
solve(dep-1,trie[node][1],bigth);
solve(dep-1,trie[node][0],bigth);
}else
if (trie[node][1])
solve(dep-1,trie[node][1], bigth| ( 1 << (dep-1) ) );else
if (trie[node][0])
solve(dep-1,trie[node][0], bigth| ( 1 << (dep-1) ) );
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d" , &a[i]);
a[i] ^= a[i-1];
}
for (int i = 0;i<=m;i++)
{
int tmp = Rieman_Lebel_Change(a[i]) ^ (a[m] ^ a[i]);
insert(tmp);
}
solve(n,0,0);
printf("%lld\n%lld",ans,anscnt);
}
D4 T1:Mayuri
就是约瑟夫问题,可以有 O(min(n,m)) 或 O(mlogn) 的,又因为m小,所以用第一个好一点。
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int size = 10005;
ll dfs2(ll,ll);
ll T,n,m;
ll dfs(ll n)
{
if (n==1) return 0;else
return ( dfs( n - 1 ) + m ) %n;
}
int main()
{
scanf("%lld",&T);
while (T--)
{
scanf("%lld%lld",&n,&m);
printf("%lld\n",dfs2(n,m)+1);
}
return 0;
}
ll dfs2( ll n, ll m )
{
ll p = 0;
if (m==1) return n-1;
for (ll i=2;i<=n;i++)
{
(p+=m)%=i;
ll x = (i-p) / (m-1) ;
if (x+ielse{p+=m*(n-i);i=n;}
}
return p%n;
}
D4 T2:Kurisu
化简之后变成这个
然后bit就好啦。
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int size = 1000005;
const ll mod = 20170927;
ll x[size],y[size];
ll x2[size],y2[size],xy2[size],xy[size];
ll n,m,dod;
ll lowbit(ll x){return (x&(-x));}
void update(ll x,ll *c,ll del)
{
while (x<=n)
{
(c[x] += del)%mod;
x+=lowbit(x);
}
}
ll query(ll x,ll *c)
{
ll ans=0;
while (x)
{
(ans+=c[x])%=mod;
x-=lowbit(x);
}
return ans%mod;
}
int main()
{
//freopen("kurisu.in","r",stdin);
//freopen("kurisu.out","w",stdout);
scanf("%lld%lld",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%lld%lld",&x[i],&y[i]);
update(i,x2,x[i]*x[i]%mod);
update(i,y2,y[i]*y[i]%mod);
update(i,xy,x[i]*y[i]%mod);
}
for (int i=1;i<=m;i++)
{
scanf("%lld",&dod);
if (dod==1)
{
ll p,xx,yy;
scanf("%lld%lld%lld",&p,&xx,&yy);
update(p,x2,xx*xx-x[p]*x[p]);
update(p,y2,yy*yy-y[p]*y[p]);
update(p,xy,(xx*yy-x[p]*y[p]+mod)%mod);
x[p]=xx;y[p]=yy;
}
else
if (dod==2)
{
ll l,r;
scanf("%lld%lld",&l,&r);
//printf("%lld%lld\n",l,r);
ll sx = (query(r,x2) - query(l-1,x2))%mod;
ll sy = (query(r,y2) - query(l-1,y2))%mod;
ll sxy = (query(r,xy) - query(l-1,xy))%mod;
ll ans = ( (sx*sy%mod - sxy*sxy%mod)+mod)%mod;
(ans +=mod)%=mod;
printf("%lld\n",ans);
}
}
return 0;
}
D4 T3:Okarin
就是一个最长公共上升子序列。
(无输出方案版)
#include
#include
using namespace std;
typedef long long ll;
const ll size = 5005;
ll dp[size],a[size],b[size];
ll n,m;
int main()
{
scanf("%lld",&n);
for (int i=1;i <= n;i++) scanf("%lld",&a[i]);
scanf("%lld",&m);
for (int i=1;i <= m;i++) scanf("%lld",&b[i]);
for (int i=1;i <= n;i++)
{
ll maxn=0;
for (int j = 1 ; j <= m ; j++)
{
if (a[i] == b[j]) dp[j] = maxn+1;
else
if (a[i] > b[j]) maxn = max(maxn , dp[j]);
}
}
ll ans=0;
for (ll i = 1 ; i <= m ; i++)
ans = max(ans , dp[i]);
printf("%lld",ans);
}
D5 T1:Adore
就是数位dp,0表示这位是偶数,1表示这位是奇数。
#include
#include
#include
using namespace std;
typedef long long ll;
const int size = 100005 , mod = 998244353 ;
int f[2][2048];
int pre[20],aft[20],cnt[2048] ;//
int m,k;
int main()
{
scanf("%d%d",&m,&k);
int now = 0;
for (int i = 1 ; i <= k ; i++)
{
int tmp;scanf("%d",&tmp);
now |= tmp << ( i - 1 );
}
for (int i = 1 ; i <= 2047 ; i++) cnt[i] = cnt[i >> 1 ] ^ (i & 1);
int t = 0; f[t][now] = 1 ; int maxn = ( 1 << k ) - 1 ;
for (int I = 2 ; I <= m-2 ; I++)
{
memset(pre , 0 , sizeof pre);memset(aft , 0 , sizeof aft);
t ^= 1;memset(f[t],0,sizeof f[t]);
for (int i = 1 ; i <= k ; i++)
for (int j = 1 ; j <= k ; j++)
{
int tmp; scanf("%d",&tmp);
pre[j] |= tmp << ( i - 1 ) ;
aft[i] |= tmp << ( j - 1 ) ;
}
for (int i = 0 ; i <= maxn ; i++)
if (f[t^1][i])
{
int s0=0 , s1=0;
for (int j = 1 ; j <= k ; j++)
{
s0 |= cnt[pre[j] & i] << (j-1);
s1 |= cnt[aft[j] & i] <<(j-1);
}
( f[t][s0] += f[t^1][i] )%= mod;
( f[t][s1] += f[t^1][i] )%= mod;
}
}
int wtf = 0;
for (int i=1;i<=k;i++)
{
int tmp;scanf("%d",&tmp);
wtf |= tmp << ( i - 1 ) ;
}
int ans = 0;
for (int i=0;i<=maxn;i++)
if (cnt[i & wtf ] == 0) (ans += f[t][i])%=mod;
printf("%d",ans);
}
D5 T2:Confess
乱搞,随机选n对就好啦(我绝对不会告诉你,倒着搜就不用随机啦)。证明:证明?我们不妨算一算如果随机选两个集合,他们交的期望 min(Σ2ni=1C(ci,2)C(n+1,2)|Σ2ni=1ci=n(n+1))=n−12 注意n 是偶数,所以大于 n−12 即可能会有交为n 的,由于大了一个常数,所以至少有 n 对!随机 O(n) 对即可。
这是STD,我的是倒着搜的,要遭hack,就不放啦。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define dep(i,a,b) for(int i = a; i >= b; i--)
#define Rep(i,a) for(int i = 0; i < a; i++)
#define pb(a) push_back(a)
#define mp(a,b) make_pair(a,b)
#define ab(x) ((x) < 0 ? -(x) : (x))
using namespace std;
typedef long long LL;
typedef map<int, int>::iterator mit;
typedef set<int>::iterator sit;
const int N = 6010;
int n;
bitset<2 * N> a[N];
void init() {
scanf("%d",&n); int m = (n * 2 - 1) / 6 + 1;
rep(i,1,n + 1) {
rep(j,1,m) {
char c = getchar(); while (c < 33) c = getchar();
c -= 33;
Rep(d,6) a[i][(j - 1) * 6 + d] = c >> d & 1;
}
}
}
bool check() {
int x = rand() % (n + 1) + 1, y = rand() % n + 1; if (y >= x) y++;
if ((int)(a[x] & a[y]).count() >= n / 2) { printf("%d %d\n",x,y); return true; } else return false;
}
int main() {
freopen("confess.in","r",stdin);
freopen("confess.out","w",stdout);
srand(233); init();
int t = 3 * n; if (t < 100) t = 100;
rep(i,1,t) if (check()) return 0;
return 0;
}
D5 T3:Repulsed
就是贪心一样的 dp ,我们只配那些距离为k的房间与灭火器。考虑自底向上贪心。 G[x][k] 表示 x 下面距离为 k 的需要灭火器的房间数, F[x][k] 表示 x 下面距离为 k 的多余灭火器数,每个灭火器和房间的匹配在 lca 处理每次假设子树里已经最优了,那么 G[x][k] 一定要用 F[x][0] 填满。
#include
#include
using namespace std;
const int size = 100005;
typedef long long ll;
ll n,s,k;
ll ans = 0;
struct EDGE{
ll to,next;
}edge[ 2 * size];ll cnt=0;
ll fa[size] , head[size];
ll f[size][30],g[size][30];
void adde(ll fr,ll to)
{
edge[++cnt]=(EDGE){to,head[fr]};
head[fr] = cnt;
}
void addedge(ll fr,ll to)
{
adde(fr,to);
adde(to,fr);
}
ll calc(ll x) {return (x/s + ( ( x % s ) > 0 ) ); }
ll dfs1(ll node,ll Fe)
{
fa[node] = Fe;
for (ll j =head[node];j;j = edge[j].next)
if (edge[j].to!= Fe) dfs1(edge[j].to,node);
}
ll dfs2(ll node)
{
for (ll j=head[node];j;j=edge[j].next)
{
if (edge[j].to!=fa[node])
{
dfs2(edge[j].to);
for (ll i = 1;i <= k;i++)
{
f[node][i] = min(n , f[node][i] + f[edge[j].to][i-1]);
g[node][i] += g[edge[j].to][i-1];
}
}
}
g[node][0]++;
if (g[node][k])
{
ll need = calc(g[node][k]);
ans += need;
f[node][0] = min (need * s , n);
}
for (ll i=0;i<=k;i++)
{
ll j = min(g[node][i],f[node][k-i]);
g[node][i] -= j; f[node][k-i] -= j;
}
for (ll i=0;i<=k-1;i++)
{
ll j = min(g[node][i],f[node][k-1-i]);
g[node][i] -= j; f[node][k-1-i] -= j;
}
}
int main()
{
scanf("%lld%lld%lld",&n,&s,&k);
for (ll i=1;ito;
scanf("%lld%lld",&fr,&to);
addedge(fr,to);
}
dfs1(1,0);
dfs2(1);
for (ll i=0;i<=k;i++)
for (ll j=0;j<=k;j++)
{
if (i+j<=k)
{
ll need = min(f[1][i],g[1][j]);
f[1][i] -= need; g[1][j] -= need;
}
}
ll cnt=0;
for (ll i=0;i<=k;i++) cnt+=g[1][i];
ans += calc(cnt);
printf("%lld",ans);
}
D6 T1:starway
好题。你考虑如果现在我们定一个答案,那么我们人就可以变成一个园啦,如果走不动,那么拦住他的就是一条路径,那么每个点再与上下边界连表,就变成了最小生成树。这是完全图,用 prim ,然后边存不下,就可以先不存,扫的时候在算,然后更新后才加边。这样还不用打标记,造好了图直接跑就可以啦。
//看我程序多美观
//美观啊,太美观了!!!
#include
#include
#include
#include
#define f(x,y,z) for (int (x) = (y) ; (x) <= (z) ; (x)++)
#define d(x,y,z) for (int (x) = (y) ; (x) >= (z) ; (x)--)
using namespace std;
typedef long long ll;
const ll size = 6010;
int n , m , k ;
struct EDGE {
ll to , next;
ll val;
} edge[2 * size]; ll cnt = 0;
ll d[size];
ll head[size] , pre[size] , x[size] , y[size];
bool vis[size];
ll ans = 0 ;
void adde(ll fr , ll to , ll val)
{
edge[++cnt] = (EDGE){ to , head[fr] , val};
head[fr] = cnt;
}
void addedge(ll fr , ll to , ll val)
{
adde(fr , to , val );
adde(to , fr , val );
}
ll dis(ll a , ll b)
{
if (a > b) swap(a , b);
if (a == k + 1 && b == k + 2) return 1ll * m * m ;
else if (b == k + 1) return 1ll * y[a] * y[a] ;
else if (b == k + 2) return 1ll * ( m - y[a] ) * ( m - y[a] ) ;
else return 1ll * ( x[a] - x[b] ) * ( x[a] - x[b] ) + 1ll * ( y[a] - y[b] ) * ( y[a] - y[b] ) ;
}
void dfs(ll node , ll f , ll c)
{
//printf("%d %d\n",node,c);
if (node == k + 2) { ans = c; return ;}else
for (ll j = head[node] ; j ; j = edge[j].next)
if ( edge[j].to != f) dfs( edge[j].to , node , max( c , edge[j].val ) );
}
int main()
{
//freopen("starway.in","r",stdin);
//freopen("starway23.out","w",stdout);
scanf("%lld %lld %lld" , &n , &m , &k );
const ll inf = 1ll * m * m + 10;
for (ll i = 1 ; i <= k ; i++) scanf("%lld %lld" , &x[i] , &y[i] );
for (ll i = 1 ; i <= k + 2 ; i++) d[i] = inf;d[k + 1] = 0;
memset(vis , false , sizeof vis);
for (ll t = 1 ; t <= k + 2 ; t++)
{
ll minm = inf ; ll flag = 0;
for (ll i = 1 ; i <= k + 2 ; i++)
if ( minm > d[i] && !vis[i] )
{
minm = d[i];
flag = i;
}
if ( pre[flag] ) addedge(pre[flag] , flag , d[flag]);
vis[flag] = 1;
for (ll i = 1 ;i <= k + 2 ; i++)
if ( dis( flag , i ) < d[i] && !vis[i] )
{
d[i] = dis( flag , i );
pre[i] = flag;
}
}
dfs(k + 1 , 0 , 0);
printf( "%.9lf\n" , sqrt(ans) / 2 );
//for (ll i=1;i<=cnt;i++)
//printf("%d %lld\n",edge[i].to,edge[i].val);
return 0;
}
D6 T2:knows
其实就是极长上升序列考虑 O(n2) 的 dp , fi 表示左边最后一个选的是谁,每次转移的时候枚举一个前面既不相交,又能保证极长的 j 转移这样的j一定是个上升序列,线段树维护上升序列即可。复杂度为 O(nlog2n) 。
#include
#include
using namespace std;
typedef long long ll;
typedef pair <int , int> Node;
const int size = 200050;
const int inf = 2002010910;
int n , p[size] , w[size] , f[size];
Node tr[size * 4] , wtf[size * 4];
#define mp make_pair
#define mid ( ( l + r ) >> 1 )
Node operator + (const Node &a , const Node &b){
if (b.first == inf) return a; else
return mp( b.first , min(a.second , b.second) );
}
void build(int node , int l , int r)
{
tr[node] = wtf[node] = mp(inf , inf);
if (l == r) return ;
build(node * 2 , l , mid);
build(node * 2 + 1 , mid + 1 , r);
}
Node calc (int node , int l , int r , Node x)
{
if (l == r)
return x > tr[node] ? tr[node] : mp(inf,inf);
if (tr[node * 2] < x)
return calc(node * 2 , l , mid , x) + wtf[node]; else
return calc(node * 2 + 1 , mid + 1 , r , x) ;
}
void update(int node,int l,int r)
{
wtf[node] = calc(node * 2 + 1 , mid + 1 , r , tr[node*2]);
tr[node] = tr[node * 2] + wtf[node];
}
void modify(int node,int l,int r,int p,Node x)
{
if (l == r)
{
tr[node] = x;
return ;
}
if (p <= mid)
modify(node * 2 , l , mid , p , x);
else
modify(node * 2 + 1 , mid + 1 , r , p , x);
update(node , l , r);
}
void query(int node,int l,int r,int ql,int qr,Node &x)
{
if (l >= ql && r <= qr){
x = x+ calc(node , l , r , x);
return ;
}
if (ql <= mid) query(node * 2 , l , mid , ql , qr , x);
if (qr >= mid + 1) query(node * 2 + 1 , mid + 1 , r , ql , qr , x);
}
#define trr stf
#define node Node
int main()
{
scanf("%d" , &n);
build(1, 1, n);
for (int i = 1; i <= n; i++)
scanf("%d", p + i);
for (int i = 1; i <= n; i++)
scanf("%d", w + i);
for (int i = n; i >= 1; i--) {
Node cur = make_pair(inf, inf);
query(1, 1, n, p[i], n, cur);
f[i] = (cur.first < inf) ? (w[i] + cur.second) : w[i];
cur = make_pair(i, f[i]);
modify(1, 1, n, p[i], cur);
}
Node ans = make_pair(inf, inf);
query(1 , 1 , n, 1, n, ans);
printf("%d", ans.second);
return 0;
}
D6 T3:lost
维护凸包,二分弹栈。
#include
#include
using namespace std;
typedef long long ll;
const ll size = 500006;
ll st[size][22];
ll d[size] , c[size] , fa[size] , ans[size];
ll n;
void wei_dfs(ll node)
{
if (node == 0) d[node] = 0 ; else d[node] = d[fa[node] ] + 1 ;
ll cur = fa[node] ;
for (ll i = 21 ;i >= 0 ; i--)//这里数字只要比 log (n)大就可以了
{
ll to = st[cur][i];if (to == 0) continue;
ll nowzi = c[to] - c[node] , nowfa = d[node] - d[to];
ll fazi = c[ st[to][0] ] - c[node] , fafa = d[node] - d[ st[to][0] ];
if (fafa * nowzi >= fazi * nowfa) cur = st[to][0];
//(c_v - c_cur) / (d_u - d_v) 的 min) 即 (c_v - c_cur) / (d_v - d_u) 的max 算了,要移项变号 麻烦啊
}
ll to = cur;
ll nowzi = c[to] - c[node] , nowfa = d[node] - d[to];
ll fazi = c[ st[to][0] ] - c[node] , fafa = d[node] - d[ st[to][0] ];//此处巨坑 注意是与node比较 而不是单调队列优化那样 害我检查了30min
if (fafa * nowzi >= fazi * nowfa) cur = st[to][0];
ans[node] = cur ; st[node][0] = cur ;//更新答案
for (ll i = 1 ; i <= 21 ; i++)
st[node][i] = st[ st[node][i-1] ][i - 1];
}
int main()
{
//freopen("lost.in","r",stdin);
//freopen("lost2333.out","w",stdout);
scanf("%lld",&n);
for (ll i = 0 ; i < n ; i++)//我发现好像必须要移这个位
scanf("%lld" , &c[i]);
for (ll i = 1 ; i < n ; i++)
scanf("%lld" , &fa[i]) , fa[i]--;fa[0] = -1;
for (ll i = 1 ; i < n ; i++)
wei_dfs(i);
for (ll i = 1; i < n; ++i)
printf("%.10lf\n", (double) (c[ans[i]] - c[i]) / (d[i] - d[ans[i]]) );
}
D7 T1 :conjucate
期望可加性,考虑每个在第一个数之前被抓的概率,并且与其他元素无关,有独立性,所以有第i堆的贡献是: aiai+a1 。
#include
double ans = 1;
long long n,a[100010];
int main()
{
scanf("%lld",&n);
for (int i = 1;i<=n;i++)
scanf("%lld",&a[i]);
for (int i=2;i<=n;i++)
ans += (double) a[i]/(double)(a[1]+a[i]);
printf("%.12lf",ans);
}
D7 T2:conjuct
乱搞dp lcs。
//做不来啦、、、粘的STD
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define dep(i,a,b) for(int i = a; i >= b; i--)
#define Rep(i,a) for(int i = 0; i < a; i++)
#define pb(a) push_back(a)
#define mp(a,b) make_pair(a,b)
#define ab(x) ((x) < 0 ? -(x) : (x))
using namespace std;
typedef long long LL;
typedef map<int, int>::iterator mit;
typedef set<int>::iterator sit;
typedef pair<int, int> pii;
#define x first
#define y second
const int N = 5010, inf = 1e6 + 10;
int T, n, f[N][2][2], g[3][N][2][2], a[N];
void upd(int &a, int b) { a = (b > a) ? b : a; }
void work() {
scanf("%d",&n); int tot = 0, c0 = 0, c1 = 0, c2 = 0;
rep(i,1,n) scanf("%d",a + i), tot += (a[i] == 2), c0 += (a[i] == 0), c1 += (a[i] == 1), c2 += (a[i] == 2);
if (!c0 || !c1) { printf("0\n"); return; }
if (!c2) { printf("-1\n"); return; }
rep(i,0,2) rep(j,0,n) Rep(d0,2) Rep(d1,2) g[i][j][d0][d1] = -inf;
g[2][0][0][0] = 0;
int ans = 0;
rep(i,1,n) {
rep(j,0,n) Rep(d0,2) Rep(d1,2) f[j][d0][d1] = -inf;
Rep(d,3) rep(j,0,i - 1) Rep(d0,2) Rep(d1,2) {
int D0 = d0 || (a[i] == 0), D1 = d1 || (a[i] == 1);
if (d == 2 && a[i] == 2) D0 = true, D1 = true;
if ((a[i] ^ d) == 1 || (a[i] == 2))
f[j + 1][D0][D1] = max(f[j + 1][D0][D1], g[d][j][d0][d1] + 1);
else f[j][D0][D1] = max(f[j][D0][D1], g[d][j][d0][d1] + 1);
}
rep(j,0,i) Rep(d0,2) Rep(d1, 2) g[a[i]][j][d0][d1] = max(g[a[i]][j][d0][d1], f[j][d0][d1]);
rep(j,0,tot) ans = max(ans, max(f[j][0][0], f[j][1][1]));
rep(j,0,tot - 1) ans = max(ans, max(f[j][0][1], f[j][1][0]));
if (a[i] == 2) ans = max(ans, max(f[tot][0][1], f[tot][1][0]));
}
printf("%d\n",n - ans);
}
int main() {
scanf("%d",&T);
while (T--) work();
return 0;
}
D7 T3:conjecture
乱搞。
D8 T1 graph:
贪心,把图建成树,然后每次贪心取,注意到我们应该在这个点的边数为偶数时全取,奇数时向下再取一个。
#include
#include
using namespace std;
const int maxn = 100050;
struct edge {
int to, vis, opp;
};
struct node {
int to, real;
};
vector G[maxn];
vector F[maxn];
int vis[maxn];
int siz[maxn];
int ans[maxn][3], ansn;
int n, m;
int read() {
int a = 0, c = 0, w = 1;
while(c < '0' || c > '9') {if(c == '-') w = -1; c = getchar();}
while(c >= '0' && c <= '9') {a = a * 10 + c - 48; c = getchar();}
return a * w;
}
int dfs1(int x) {
vis[x] = 1;
F[x].clear();
for(int i = 0; i < G[x].size(); i++) if(!G[x][i].vis) {
G[x][i].vis = 1;
G[G[x][i].to][G[x][i].opp].vis = 1;
if(!vis[G[x][i].to]) {
F[x].push_back((node){G[x][i].to, -1});
siz[x] += dfs1(G[x][i].to) + 1;
}else{
F[x].push_back((node){n, G[x][i].to});
siz[x] += 1;
}
}
return siz[x];
}
void dfs2(int x, int odd, int fa) {
if(odd == 0) {
int single = 0, p;
for(int i = 0; i < F[x].size(); i++) {
int v = F[x][i].to;
if(siz[v] % 2 == 0) {
single++;
if(single == 1) p = (F[x][i].real != -1 ? F[x][i].real : v);
else{
single = 0;
ans[ansn][0] = (F[x][i].real != -1 ? F[x][i].real : v);
ans[ansn][1] = x;
ans[ansn++][2] = p;
}
dfs2(v, 0, x);
}else dfs2(v, 1, x);
}
}else{
int hungry = 1, single = 0, p;
for(int i = 0; i < F[x].size(); i++) {
int v = F[x][i].to;
if(hungry && siz[v] % 2 == 0) {
hungry = 0;
ans[ansn][0] = fa;
ans[ansn][1] = x;
ans[ansn++][2] = (F[x][i].real != -1 ? F[x][i].real : v);
dfs2(v, 0, x);
}else if(siz[v] % 2 == 0) {
single++;
if(single == 1) p = (F[x][i].real != -1 ? F[x][i].real : v);
else{
single = 0;
ans[ansn][0] = (F[x][i].real != -1 ? F[x][i].real : v);
ans[ansn][1] = x;
ans[ansn++][2] = p;
}
dfs2(v, 0, x);
}else dfs2(v, 1, x);
}
}
}
int main() {
n = read(); m = read();
for(int i = 0; i < m; i++) {
int from = read()-1, to = read()-1;
G[from].push_back((edge){to, 0, G[to].size()});
G[to].push_back((edge){from, 0, G[from].size()-1});
}
for(int i = 0; i < n; i++) if(!vis[i]) {
dfs1(i);
dfs2(i, 0, -1);
}
printf("%d\n", ansn);
for(int i = 0; i < ansn; i++) printf("%d %d %d\n", ans[i][0]+1, ans[i][1]+1, ans[i][2]+1);
return 0;
}
D8 T2 permutation:
注意到我们先反一下原数列,构造 p[i] 表示i现在在原来的那个数列,所以现在交换就变成了相邻交换。然后有些距离小于 k 的两个的相对位置不会变,所以靠此建图,拓扑排序,dfs一遍就好了。
#include
using namespace std;
int a[500010],pos[500010];
bool fl;
const int maxn=500010;
int n,k,p[maxn],q[maxn],deg[maxn];
int tote,FIR[maxn],TO[maxn<<1],NEXT[maxn<<1];
priority_queue<int> pq;
namespace SegTree{
int mn[maxn<<2];
#define lc (nd<<1)
#define rc (nd<<1|1)
#define mid ((s+t)>>1)
void init()
{
memset(mn,0x3f3f3f3f,sizeof(mn));
}
void update(int nd,int s,int t,int id,int val)
{
if (s==t) {mn[nd]=val; return;}
if (id<=mid) update(lc,s,mid,id,val);
else update(rc,mid+1,t,id,val);
mn[nd]=min(mn[lc],mn[rc]);
}
int query(int nd,int s,int t,int l,int r)
{
if (l<=s&&t<=r) return mn[nd];
int Ans=0x7fffffff;
if (l<=mid) Ans=min(Ans,query(lc,s,mid,l,r));
if (r> mid) Ans=min(Ans,query(rc,mid+1,t,l,r));
return Ans;
}
}
void addedge(int u,int v)
{
TO[++tote]=v;
NEXT[tote]=FIR[u];
FIR[u]=tote;
deg[v]++;
}
void read(int &x)
{
char c=getchar(); x=0;
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
}
int main()
{
//freopen("permutation.in","r",stdin);
//freopen("permutation.out","w",stdout);
scanf("%d",&n);scanf("%d",&k);
if (n<=10000)
{
for(int i=1;i<=n;++i)scanf("%d",&a[i]),pos[a[i]]=i;
if(k==1)
{
for(int i=1;i<=n;++i)printf("%d\n",i);
return 0;
}
fl=1;
while(fl)
{
fl=0;
for(int i=1;i<=n;++i)
{
for(int j=a[i]-1;j>0;--j)
{
if(pos[j]pos[j]-ibreak;
if(!fl)fl=1;
swap(pos[a[i]],pos[j]);
swap(a[pos[a[i]]],a[pos[j]]);
}
}
}
for(int i=1;i<=n;++i)printf("%d\n",a[i]);
}
else
{int i,x;
for (i=1;i<=n;i++)
read(p[i]),q[p[i]]=i;
SegTree::init();
for (i=n;i>=1;i--)
{
x=SegTree::query(1,1,n,q[i]-k+1,q[i]);
if (x<=n) addedge(q[x],q[i]);
x=SegTree::query(1,1,n,q[i],q[i]+k-1);
if (x<=n) addedge(q[x],q[i]);
SegTree::update(1,1,n,q[i],i);
}
for (i=1;i<=n;i++)
if (!deg[i]) pq.push(i);
for (i=n;i>=1;i--)
{
int u=p[i]=pq.top(); pq.pop();
for (int p=FIR[u];p;p=NEXT[p])
if (!(--deg[TO[p]])) pq.push(TO[p]);
}
for (i=1;i<=n;i++) q[p[i]]=i;
for (i=1;i<=n;i++) printf("%d\n",q[i]);
}
}
D8 T3 tree:
每条边都至少出现了一次,所以最优的情况就是所有边权之和,然后能不能取到证不来,反正可以,所以加起来就好啦。(注意爆longlong)
#include
#include
#include
#include
using namespace std;
typedef long long ll;
ll n,k;
ll ans=0;
int main()
{
scanf("%lld",&n);
ll fr,to,val;
for (int i=1;iscanf("%lld%lld%lld",&fr,&to,&val) ,ans+=val;
printf("%lld",ans);
return 0;
}
这次去雅礼,总的考的不好,也许是因为是高一不稳定的原因。所以回来后要好好提升自己的知识水平,下次去AK雅礼集训。